JavaScript Math & Random: Math Properties · All Math Methods · Random Numbers · Full Reference
📑 Table of Contents
- Background: What Is the Math Object?
- Topic 1 — Math Properties (Constants)
- Topic 2 — Rounding Methods
- Topic 3 — Arithmetic & Exponent Methods
- Topic 4 — Min, Max & Clamp
- Topic 5 — Logarithm & Trigonometry Methods
- Topic 6 — Math.random() — Random Numbers
- Topic 7 — Complete Math Reference
- Applied Exercises
- Mini Project — Casino Dice & Statistics Simulator
- Completion Checklist
1. Background: What Is the Math Object?
In everyday life, you regularly need mathematical operations that go beyond simple +, -, *, /:
- Rounding a price to 2 decimal places
- Finding the largest value in a list
- Calculating a square root for geometry
- Generating a random number for a game
- Working with angles for animations
JavaScript provides the Math object — a built-in collection of mathematical constants and functions — ready to use without importing anything.
// No import needed — Math is always available
console.log(Math.PI); // 3.141592653589793
console.log(Math.sqrt(16)); // 4
console.log(Math.round(4.7)); // 5
console.log(Math.random()); // e.g. 0.7362841...
Key Facts About the Math Object
| Fact | Detail |
|---|---|
| Type | Static object — NOT a constructor |
| Usage | Math.methodName() — never new Math() |
| Properties | 8 mathematical constants |
| Methods | 30+ mathematical functions |
| Number type | All values are JavaScript 64-bit floats |
🐛 COMMON MISTAKE:
new Math()throws aTypeError. Math is not a class — it is a plain object with static properties and methods. You never instantiate it.const m = new Math(); // ❌ TypeError: Math is not a constructor const pi = Math.PI; // ✅ correct
🏢 REAL WORLD: Math methods appear in every domain of software: financial apps (rounding money), games (random events, physics), maps (distance calculations using trigonometry), data science (statistics), graphics (transformations), and UI animations (easing curves).
2. Topic 1 — Math Properties (Constants)
Phase 1 — Conceptual Understanding
The Math object has 8 built-in constants — mathematical values that are used so frequently in real calculations that JavaScript provides them pre-computed to full precision.
console.log(Math.E); // 2.718281828459045 — Euler's number
console.log(Math.PI); // 3.141592653589793 — Pi
console.log(Math.SQRT2); // 1.4142135623730951 — Square root of 2
console.log(Math.SQRT1_2); // 0.7071067811865476 — Square root of 1/2
console.log(Math.LN2); // 0.6931471805599453 — Natural log of 2
console.log(Math.LN10); // 2.302585092994046 — Natural log of 10
console.log(Math.LOG2E); // 1.4426950408889634 — Log base 2 of E
console.log(Math.LOG10E); // 0.4342944819032518 — Log base 10 of E
▶ Expected Output:
2.718281828459045
3.141592653589793
1.4142135623730951
0.7071067811865476
0.6931471805599453
2.302585092994046
1.4426950408889634
0.4342944819032518
The Most Important Constant: Math.PI
Math.PI (π ≈ 3.14159…) is used in every circular and angular calculation:
// Area of a circle: A = π × r²
function circleArea(radius) {
return Math.PI * radius ** 2;
}
console.log(circleArea(5).toFixed(2)); // 78.54
// Circumference: C = 2 × π × r
function circumference(radius) {
return 2 * Math.PI * radius;
}
console.log(circumference(5).toFixed(2)); // 31.42
▶ Expected Output:
78.54
31.42
Math.E — Euler’s Number
Math.E (e ≈ 2.71828…) is the base of the natural logarithm. It appears in compound interest, population growth, and probability:
// Compound interest: A = P × e^(r × t)
function continuousCompound(principal, rate, years) {
return principal * Math.E ** (rate * years);
}
// $1000 at 5% for 10 years
console.log(continuousCompound(1000, 0.05, 10).toFixed(2)); // 1648.72
▶ Expected Output: 1648.72
All 8 Math Constants at a Glance
| Constant | Value | Common Use |
|---|---|---|
Math.E |
2.718… | Exponential growth, natural log |
Math.PI |
3.141… | Circles, angles, trigonometry |
Math.SQRT2 |
1.414… | Diagonal of a unit square |
Math.SQRT1_2 |
0.707… | Reciprocal of √2 |
Math.LN2 |
0.693… | Converting between log bases |
Math.LN10 |
2.302… | Converting natural log to log₁₀ |
Math.LOG2E |
1.442… | Log base 2 of Euler’s number |
Math.LOG10E |
0.434… | Log base 10 of Euler’s number |
3. Topic 2 — Rounding Methods
Phase 1 — Conceptual Understanding
Rounding is one of the most frequently used mathematical operations in real apps — displaying prices, scores, ratings, and measurements all require rounding. JavaScript provides four distinct rounding methods, each with a specific behaviour.
The Four Rounding Methods — Side by Side
Before diving in, see all four on the same number:
const n = 4.6;
console.log(Math.round(n)); // 5 — nearest integer (rounds .5 up)
console.log(Math.floor(n)); // 4 — always rounds DOWN (toward -∞)
console.log(Math.ceil(n)); // 5 — always rounds UP (toward +∞)
console.log(Math.trunc(n)); // 4 — always removes decimal (toward 0)
▶ Expected Output:
5
4
5
4
Now watch how they differ for negative numbers — this is where beginners are often surprised:
const n = -4.6;
console.log(Math.round(n)); // -5 — rounds to nearest (-4.6 → -5)
console.log(Math.floor(n)); // -5 — DOWN means MORE negative
console.log(Math.ceil(n)); // -4 — UP means LESS negative
console.log(Math.trunc(n)); // -4 — just removes the decimal
▶ Expected Output:
-5
-5
-4
-4
🤔 THINK ABOUT IT: For negative numbers, “floor” (down) means more negative, and “ceil” (up) means less negative. Think of a number line — floor always goes left, ceil always goes right, trunc always goes toward zero.
Math.round(x) — Round to Nearest Integer
Rounds to the nearest whole number. If the decimal is exactly .5, it rounds UP (away from zero for positive numbers).
console.log(Math.round(4.1)); // 4
console.log(Math.round(4.5)); // 5 ← .5 rounds up
console.log(Math.round(4.9)); // 5
console.log(Math.round(-4.5)); // -4 ← .5 rounds TOWARD zero for negatives
▶ Expected Output:
4
5
5
-4
Real use — round to decimal places:
JavaScript’s Math.round only rounds to whole numbers. To round to a specific number of decimal places, multiply, round, then divide:
// Round to 2 decimal places
function roundTo(value, decimals) {
const factor = 10 ** decimals;
return Math.round(value * factor) / factor;
}
console.log(roundTo(3.14159, 2)); // 3.14
console.log(roundTo(2.5678, 1)); // 2.6
console.log(roundTo(1.005, 2)); // 1.01 (note: floating-point edge case)
💡 TIP:
toFixed(n)is usually cleaner for displaying rounded numbers as strings:console.log((3.14159).toFixed(2)); // "3.14" ← returns a STRING console.log(+(3.14159).toFixed(2)); // 3.14 ← the + converts back to Number
Math.floor(x) — Always Round Down
Rounds toward negative infinity — the largest integer less than or equal to x.
console.log(Math.floor(4.1)); // 4
console.log(Math.floor(4.9)); // 4 ← still 4, not 5!
console.log(Math.floor(4.0)); // 4
console.log(Math.floor(-4.1)); // -5 ← goes MORE negative
console.log(Math.floor(-4.9)); // -5
▶ Expected Output:
4
4
4
-5
-5
🏢 REAL WORLD:
Math.flooris the go-to for random integer generation (covered in Topic 6) and converting a time in seconds to whole minutes:Math.floor(seconds / 60).
Math.ceil(x) — Always Round Up
Rounds toward positive infinity — the smallest integer greater than or equal to x.
console.log(Math.ceil(4.1)); // 5
console.log(Math.ceil(4.9)); // 5
console.log(Math.ceil(4.0)); // 4 ← already an integer, no change
console.log(Math.ceil(-4.1)); // -4 ← goes LESS negative (toward zero)
console.log(Math.ceil(-4.9)); // -4
▶ Expected Output:
5
5
4
-4
-4
🏢 REAL WORLD:
Math.ceilis used for pagination — “how many pages do I need for 97 items with 10 per page?” →Math.ceil(97 / 10)= 10 pages.
Math.trunc(x) — Remove Decimal (Truncate Toward Zero)
Simply removes the fractional part — always rounds toward zero regardless of sign.
console.log(Math.trunc(4.9)); // 4 ← positive: rounds down
console.log(Math.trunc(4.1)); // 4
console.log(Math.trunc(-4.9)); // -4 ← negative: rounds up (toward zero)
console.log(Math.trunc(-4.1)); // -4
console.log(Math.trunc(0.9)); // 0
▶ Expected Output:
4
4
-4
-4
0
💡 TIP: The difference between
Math.floorandMath.trunconly matters for negative numbers:
Math.floor(-4.7)→-5(goes more negative)Math.trunc(-4.7)→-4(goes toward zero) For positive numbers they always give the same result.
Rounding Summary Table
| Method | Positive 4.6 | Positive 4.4 | Negative -4.6 | Negative -4.4 | Rule |
|---|---|---|---|---|---|
round |
5 | 4 | -5 | -4 | Nearest (.5 → up) |
floor |
4 | 4 | -5 | -5 | Always toward -∞ |
ceil |
5 | 5 | -4 | -4 | Always toward +∞ |
trunc |
4 | 4 | -4 | -4 | Always toward 0 |
4. Topic 3 — Arithmetic & Exponent Methods
Phase 1 — Conceptual Understanding
Math.abs(x) — Absolute Value
Returns the positive (absolute) value of any number — removes the sign.
console.log(Math.abs(5)); // 5
console.log(Math.abs(-5)); // 5
console.log(Math.abs(0)); // 0
console.log(Math.abs(-3.7)); // 3.7
▶ Expected Output:
5
5
0
3.7
🏢 REAL WORLD: Calculating distance between two values on a scale:
Math.abs(score - average)— the difference doesn’t depend on which is bigger. Also used in physics (speed is the absolute value of velocity).
Math.sqrt(x) — Square Root
Returns the square root of x. Returns NaN for negative numbers (complex numbers are not supported).
console.log(Math.sqrt(9)); // 3
console.log(Math.sqrt(2)); // 1.4142135623730951
console.log(Math.sqrt(0)); // 0
console.log(Math.sqrt(-1)); // NaN ← negative: no real square root
console.log(Math.sqrt(144)); // 12
▶ Expected Output:
3
1.4142135623730951
0
NaN
12
🏢 REAL WORLD: Distance between two points:
Math.sqrt((x2-x1)**2 + (y2-y1)**2)— the Pythagorean theorem in code, used in maps, games, and physics engines.
function distance(x1, y1, x2, y2) {
return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
}
console.log(distance(0, 0, 3, 4).toFixed(2)); // 5.00 (3-4-5 triangle)
Math.cbrt(x) — Cube Root
Returns the cube root of x. Unlike sqrt, it works for negative numbers.
console.log(Math.cbrt(27)); // 3
console.log(Math.cbrt(-27)); // -3 ← works for negatives!
console.log(Math.cbrt(8)); // 2
console.log(Math.cbrt(0)); // 0
▶ Expected Output:
3
-3
2
0
Math.pow(base, exponent) — Power / Exponentiation
Raises base to the power of exponent. The ** operator is the modern equivalent.
console.log(Math.pow(2, 10)); // 1024 (2^10)
console.log(Math.pow(3, 3)); // 27 (3^3)
console.log(Math.pow(9, 0.5)); // 3 (9^0.5 = √9)
console.log(Math.pow(2, -1)); // 0.5 (1/2)
// Modern equivalent using ** operator:
console.log(2 ** 10); // 1024
▶ Expected Output:
1024
27
3
0.5
1024
Math.hypot(...values) — Hypotenuse / Euclidean Length
Returns the square root of the sum of squares of all arguments. The cleanest way to compute distances and vector lengths.
// Hypotenuse of 3-4-5 triangle
console.log(Math.hypot(3, 4)); // 5
// Distance in 3D space
console.log(Math.hypot(2, 4, 4)); // 6 (√(4+16+16) = √36)
// Length of a 2D vector
console.log(Math.hypot(-5, 12)); // 13
▶ Expected Output:
5
6
13
💡 TIP:
Math.hypot(a, b)is cleaner and avoids overflow issues compared toMath.sqrt(a**2 + b**2).
Math.sign(x) — Sign of a Number
Returns -1 (negative), 0 (zero), or 1 (positive). Useful to determine direction without caring about magnitude.
console.log(Math.sign(-7)); // -1
console.log(Math.sign(0)); // 0
console.log(Math.sign(3)); // 1
console.log(Math.sign(-0)); // -0 (signed zero in JavaScript)
▶ Expected Output:
-1
0
1
-0
🏢 REAL WORLD: In animation:
Math.sign(velocity)tells you which direction an object is moving without needing to know the speed. In finance:Math.sign(profit)tells you gain vs loss.
Math.fround(x) — Nearest 32-bit Float
Returns the nearest 32-bit (single precision) floating-point representation of x. Useful for WebGL and typed arrays that use 32-bit floats.
console.log(Math.fround(1.5)); // 1.5 (representable exactly)
console.log(Math.fround(1.337)); // 1.3370000123977661 (precision loss!)
Math.clz32(x) — Count Leading Zeros (32-bit)
Returns the number of leading zero bits in the 32-bit integer representation. Used in low-level bit manipulation.
console.log(Math.clz32(1)); // 31 (00000000000000000000000000000001)
console.log(Math.clz32(4)); // 29 (00000000000000000000000000000100)
console.log(Math.clz32(0)); // 32 (all zeros)
Math.imul(x, y) — 32-bit Integer Multiplication
Performs C-style 32-bit integer multiplication. Important for large integer products that would overflow normal JavaScript arithmetic.
console.log(Math.imul(3, 4)); // 12
console.log(Math.imul(0xffffffff, 0xffffffff)); // 1 (wraps around!)
5. Topic 4 — Min, Max & Clamp
Phase 1 — Conceptual Understanding
Math.max(...values) — Find the Largest Value
Returns the largest of zero or more numbers. Returns -Infinity if no arguments given. Returns NaN if any argument is not a number.
console.log(Math.max(3, 7, 1, 9, 4)); // 9
console.log(Math.max(-3, -7, -1)); // -1
console.log(Math.max()); // -Infinity (no args)
console.log(Math.max(1, "abc", 3)); // NaN
▶ Expected Output:
9
-1
-Infinity
NaN
Find max in an array using spread:
const scores = [78, 92, 65, 88, 71, 95];
console.log(Math.max(...scores)); // 95
⚠️ WATCH OUT:
Math.max(...arr)works perfectly for small arrays. For very large arrays (100,000+ elements), the spread can cause a stack overflow. Usearr.reduce((a, b) => Math.max(a, b))instead.
Math.min(...values) — Find the Smallest Value
Returns the smallest of zero or more numbers. Returns Infinity if no arguments given.
console.log(Math.min(3, 7, 1, 9, 4)); // 1
console.log(Math.min(-3, -7, -1)); // -7
console.log(Math.min()); // Infinity (no args)
const scores = [78, 92, 65, 88, 71, 95];
console.log(Math.min(...scores)); // 65
▶ Expected Output:
1
-7
Infinity
65
🤔 THINK ABOUT IT: Why does
Math.max()(no args) return-InfinityandMath.min()returnInfinity? Because when comparing values, any real number is greater than-Infinityand less thanInfinity. This makes the identity values work correctly when looping through values to find max/min.
Clamping a Value — Keep It Within a Range
A clamp function restricts a value to a specific range [min, max]. This is one of the most used utility functions in game development and UI.
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
console.log(clamp(5, 0, 10)); // 5 — within range, unchanged
console.log(clamp(-3, 0, 10)); // 0 — below min, clamped to min
console.log(clamp(15, 0, 10)); // 10 — above max, clamped to max
▶ Expected Output:
5
0
10
🏢 REAL WORLD: Clamping is everywhere in games and UIs — keeping a volume slider between 0 and 100, a health bar between 0 and 100 (cannot go negative or above max), or constraining a draggable element within its container’s boundaries.
// Game health system
let health = 100;
function takeDamage(amount) { health = clamp(health - amount, 0, 100); }
function heal(amount) { health = clamp(health + amount, 0, 100); }
takeDamage(30); console.log(health); // 70
takeDamage(90); console.log(health); // 0 ← clamped — cannot go below 0
heal(50); console.log(health); // 50
heal(200); console.log(health); // 100 ← clamped — cannot exceed 100
▶ Expected Output:
70
0
50
100
6. Topic 5 — Logarithm & Trigonometry Methods
Phase 1 — Conceptual Understanding
Logarithm Methods
Math.log(x) — Natural Logarithm (base e)
Returns the natural logarithm (base e) of x. Returns NaN for negative x and -Infinity for 0.
console.log(Math.log(1)); // 0 (e^0 = 1)
console.log(Math.log(Math.E)); // 1 (e^1 = e)
console.log(Math.log(Math.E**2)); // 2 (e^2)
console.log(Math.log(0)); // -Infinity
console.log(Math.log(-1)); // NaN
Math.log2(x) — Logarithm Base 2
console.log(Math.log2(1)); // 0 (2^0 = 1)
console.log(Math.log2(2)); // 1 (2^1 = 2)
console.log(Math.log2(8)); // 3 (2^3 = 8)
console.log(Math.log2(1024)); // 10 (2^10 = 1024)
🏢 REAL WORLD:
Math.log2is used in computer science — calculating how many bits are needed to store n values:Math.ceil(Math.log2(n)).
Math.log10(x) — Logarithm Base 10
console.log(Math.log10(1)); // 0 (10^0 = 1)
console.log(Math.log10(10)); // 1 (10^1 = 10)
console.log(Math.log10(100)); // 2 (10^2 = 100)
console.log(Math.log10(1000)); // 3
Math.expm1(x) — e^x minus 1
More accurate than Math.E**x - 1 for very small x values.
console.log(Math.expm1(1)); // 1.718... (e^1 - 1)
console.log(Math.expm1(0)); // 0 (e^0 - 1 = 0)
Math.log1p(x) — Natural log of (1 + x)
More accurate than Math.log(1 + x) for very small x values.
console.log(Math.log1p(0)); // 0
console.log(Math.log1p(1)); // 0.693... (= Math.LN2)
Trigonometry Methods
All trigonometric functions in JavaScript work in radians, not degrees.
Degrees to Radians: radians = degrees × (π / 180)
Radians to Degrees: degrees = radians × (180 / π)
function toRad(deg) { return deg * (Math.PI / 180); }
function toDeg(rad) { return rad * (180 / Math.PI); }
console.log(toRad(180)); // 3.14159... (π)
console.log(toRad(90)); // 1.5707... (π/2)
console.log(toDeg(Math.PI)); // 180
Math.sin(x) — Sine
console.log(Math.sin(toRad(0))); // 0
console.log(Math.sin(toRad(30))); // 0.5
console.log(Math.sin(toRad(90))); // 1 ← max value
console.log(Math.sin(toRad(180))); // ~0 (floating point: 1.2246e-16)
Math.cos(x) — Cosine
console.log(Math.cos(toRad(0))); // 1 ← max value
console.log(Math.cos(toRad(90))); // ~0 (floating point near-zero)
console.log(Math.cos(toRad(180))); // -1 ← min value
Math.tan(x) — Tangent
console.log(Math.tan(toRad(0))); // 0
console.log(Math.tan(toRad(45))); // 1
console.log(Math.tan(toRad(90))); // 16331239353195370 (very large — approaches ∞)
Inverse Trigonometry
console.log(toDeg(Math.asin(0.5))); // 30 (angle whose sine is 0.5)
console.log(toDeg(Math.acos(0.5))); // 60 (angle whose cosine is 0.5)
console.log(toDeg(Math.atan(1))); // 45 (angle whose tangent is 1)
// atan2(y, x) — angle from origin to point (x,y), handles all quadrants
console.log(toDeg(Math.atan2(1, 1))); // 45
console.log(toDeg(Math.atan2(1, -1))); // 135
console.log(toDeg(Math.atan2(-1, 0))); // -90
🏢 REAL WORLD:
Math.atan2(y, x)is used in game development and mapping to find the angle between two points — e.g., “which direction should the enemy face to look at the player?”
Hyperbolic Functions
console.log(Math.sinh(0)); // 0
console.log(Math.cosh(0)); // 1
console.log(Math.tanh(0)); // 0
console.log(Math.tanh(1)); // 0.7615...
// Inverse hyperbolic
console.log(Math.asinh(1)); // 0.8813...
console.log(Math.acosh(1)); // 0
console.log(Math.atanh(0)); // 0
7. Topic 6 — Math.random() — Random Numbers
Phase 1 — Conceptual Understanding
Math.random() is one of the most-used Math methods. It returns a pseudo-random floating-point number between 0 (inclusive) and 1 (exclusive):
0 ≤ Math.random() < 1
console.log(Math.random()); // e.g. 0.7362841095
console.log(Math.random()); // e.g. 0.1284956023
console.log(Math.random()); // e.g. 0.9813740561
💡 TIP:
Math.random()always returns a value in[0, 1). It will NEVER return exactly 1. Understanding this boundary is essential for all random number formulas.
⚠️ WATCH OUT — Not Cryptographically Secure:
Math.random()is a pseudo-random number generator — good enough for games, simulations, and UI randomness, but NOT suitable for cryptography, password generation, or security tokens. Usecrypto.getRandomValues()for security-sensitive random values.
Random Float in a Range
// Random float between min (inclusive) and max (exclusive)
function randomFloat(min, max) {
return Math.random() * (max - min) + min;
}
console.log(randomFloat(1, 10).toFixed(2)); // e.g. 7.43
console.log(randomFloat(0, 100).toFixed(2)); // e.g. 62.15
console.log(randomFloat(-5, 5).toFixed(2)); // e.g. -2.87
How the formula works:
Math.random() → [0, 1)
× (max - min) → [0, max-min)
+ min → [min, max)
Random Integer — The Most Important Pattern
Generating a random whole number (integer) is the most common random operation.
Random integer from 0 to n (exclusive):
// Random integer: 0, 1, 2, ..., (n-1)
function randomInt(n) {
return Math.floor(Math.random() * n);
}
console.log(randomInt(6)); // 0, 1, 2, 3, 4, or 5 (dice: 0-5)
console.log(randomInt(10)); // 0 through 9
Random integer in a range (inclusive on both ends):
// Random integer: min to max (both inclusive)
function randomIntRange(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// Dice roll (1 to 6)
console.log(randomIntRange(1, 6)); // 1, 2, 3, 4, 5, or 6
// Random score (50 to 100)
console.log(randomIntRange(50, 100));
// Coin flip
console.log(randomIntRange(0, 1) === 0 ? "Heads" : "Tails");
How (max - min + 1) works:
For min=1, max=6:
Math.random() → [0, 1)
× (6 - 1 + 1) = × 6 → [0, 6)
+ 1 → [1, 7)
Math.floor(...) → 1, 2, 3, 4, 5, or 6 ✅
🐛 COMMON MISTAKE: Using
Math.roundinstead ofMath.floorfor random integers creates an unequal distribution —Math.round(Math.random() * 5)gives 0 and 5 only half the probability of 1, 2, 3, 4. Always useMath.floorfor random integers.
Guaranteed Random Integer Functions — Reference Set
These four functions cover virtually every random integer need:
// 1. Integer from 0 to (max - 1)
const randBelow = max => Math.floor(Math.random() * max);
// 2. Integer from min to max (both inclusive)
const randRange = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
// 3. Random index for an array
const randIndex = arr => Math.floor(Math.random() * arr.length);
// 4. Random element from an array
const randElement = arr => arr[Math.floor(Math.random() * arr.length)];
// Usage:
const fruits = ["Apple", "Banana", "Mango", "Kiwi", "Orange"];
console.log(randBelow(10)); // 0–9
console.log(randRange(1, 6)); // 1–6 (dice)
console.log(randIndex(fruits)); // 0–4
console.log(randElement(fruits)); // random fruit
▶ Expected Output (sample):
7
4
2
Mango
Random Boolean
const randomBool = () => Math.random() < 0.5;
console.log(randomBool()); // true or false with equal probability
// Weighted boolean — 70% chance of true
const weighted = () => Math.random() < 0.7;
Shuffle an Array — Fisher-Yates Algorithm
The gold-standard way to randomly shuffle an array, giving every permutation equal probability:
function shuffle(array) {
const arr = [...array]; // copy — don't mutate original
for (let i = arr.length - 1; i > 0; i--) {
const j = randBelow(i + 1); // random index 0 to i
[arr[i], arr[j]] = [arr[j], arr[i]]; // swap
}
return arr;
}
const deck = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
console.log(shuffle(deck)); // e.g. [7, 3, 10, 1, 5, 8, 2, 9, 4, 6]
🏢 REAL WORLD: Used in quiz apps (shuffle questions), playlist shuffling, card games, A/B testing (randomly assign users to groups), and generating test data.
Random Item from a Weighted List
Sometimes you need random selection where some items are more likely than others:
function weightedRandom(options) {
// options = [{ value, weight }, ...]
const totalWeight = options.reduce((sum, o) => sum + o.weight, 0);
let rand = Math.random() * totalWeight;
for (const option of options) {
rand -= option.weight;
if (rand <= 0) return option.value;
}
}
const loot = [
{ value: "Common item", weight: 60 },
{ value: "Uncommon item", weight: 25 },
{ value: "Rare item", weight: 10 },
{ value: "Legendary item", weight: 5 },
];
// Run 10 times to show distribution
for (let i = 0; i < 10; i++) {
console.log(weightedRandom(loot));
}
▶ Expected Output (sample — varies):
Common item
Common item
Uncommon item
Common item
Rare item
Common item
Common item
Uncommon item
Common item
Legendary item
🏢 REAL WORLD: Weighted random selection is used in loot boxes in games, recommendation engines, ad delivery systems, and generating synthetic test data with realistic distributions.
Random Color Generator
// Random hex color
function randomHexColor() {
return "#" + Math.floor(Math.random() * 0xFFFFFF)
.toString(16)
.padStart(6, "0");
}
// Random RGB color
function randomRGBColor() {
const r = randBelow(256);
const g = randBelow(256);
const b = randBelow(256);
return `rgb(${r}, ${g}, ${b})`;
}
console.log(randomHexColor()); // e.g. "#a3f5c2"
console.log(randomRGBColor()); // e.g. "rgb(173, 42, 201)"
Seeded Random (Reproducible Results)
Math.random() is non-deterministic — you cannot reproduce the same sequence. For reproducible results (testing, games with replay), use a seeded pseudo-random generator:
// Simple mulberry32 seeded PRNG
function createSeededRandom(seed) {
let s = seed;
return function() {
s |= 0; s = s + 0x6D2B79F5 | 0;
let t = Math.imul(s ^ s >>> 15, 1 | s);
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
return ((t ^ t >>> 14) >>> 0) / 4294967296;
};
}
const rand = createSeededRandom(42);
console.log(rand().toFixed(4)); // always the same for seed 42
console.log(rand().toFixed(4)); // always the same next value
console.log(rand().toFixed(4)); // always the same third value
🏢 REAL WORLD: Game developers use seeded random to generate the same world layout from the same seed — players can share a seed code to play the same map. Minecraft uses this concept extensively.
8. Topic 7 — Complete Math Reference
Your one-stop reference for every Math property and method.
Math Properties
| Property | Value | Description |
|---|---|---|
Math.E |
2.718… | Euler’s number |
Math.PI |
3.141… | Pi |
Math.SQRT2 |
1.414… | Square root of 2 |
Math.SQRT1_2 |
0.707… | Square root of 1/2 |
Math.LN2 |
0.693… | Natural log of 2 |
Math.LN10 |
2.302… | Natural log of 10 |
Math.LOG2E |
1.442… | Log₂(e) |
Math.LOG10E |
0.434… | Log₁₀(e) |
Rounding Methods
| Method | Description | Example → Result |
|---|---|---|
Math.round(x) |
Nearest integer (.5 → up) | Math.round(4.5) → 5 |
Math.floor(x) |
Toward -∞ | Math.floor(-4.1) → -5 |
Math.ceil(x) |
Toward +∞ | Math.ceil(4.1) → 5 |
Math.trunc(x) |
Toward 0 (remove decimal) | Math.trunc(-4.9) → -4 |
Arithmetic Methods
| Method | Description | Example → Result |
|---|---|---|
Math.abs(x) |
Absolute value | Math.abs(-5) → 5 |
Math.sqrt(x) |
Square root | Math.sqrt(16) → 4 |
Math.cbrt(x) |
Cube root | Math.cbrt(-8) → -2 |
Math.pow(x, y) |
x to the power of y | Math.pow(2, 8) → 256 |
Math.hypot(...v) |
√(sum of squares) | Math.hypot(3,4) → 5 |
Math.sign(x) |
-1, 0, or 1 | Math.sign(-9) → -1 |
Math.fround(x) |
Nearest 32-bit float | Math.fround(1.337) → 1.337... |
Math.clz32(x) |
Count leading zeros (32-bit) | Math.clz32(1) → 31 |
Math.imul(x, y) |
32-bit integer multiply | Math.imul(3,4) → 12 |
Min / Max
| Method | Description | Example → Result |
|---|---|---|
Math.max(...v) |
Largest value | Math.max(3,1,7) → 7 |
Math.min(...v) |
Smallest value | Math.min(3,1,7) → 1 |
Logarithm Methods
| Method | Description | Example → Result |
|---|---|---|
Math.log(x) |
Natural log (base e) | Math.log(Math.E) → 1 |
Math.log2(x) |
Log base 2 | Math.log2(8) → 3 |
Math.log10(x) |
Log base 10 | Math.log10(1000) → 3 |
Math.exp(x) |
e raised to x | Math.exp(1) → 2.718... |
Math.expm1(x) |
e^x - 1 (precise) | Math.expm1(0) → 0 |
Math.log1p(x) |
ln(1 + x) (precise) | Math.log1p(0) → 0 |
Trigonometry Methods (all angles in radians)
| Method | Description |
|---|---|
Math.sin(x) |
Sine |
Math.cos(x) |
Cosine |
Math.tan(x) |
Tangent |
Math.asin(x) |
Arc sine (returns radians) |
Math.acos(x) |
Arc cosine (returns radians) |
Math.atan(x) |
Arc tangent (returns radians) |
Math.atan2(y, x) |
Angle from origin to (x,y) |
Math.sinh(x) |
Hyperbolic sine |
Math.cosh(x) |
Hyperbolic cosine |
Math.tanh(x) |
Hyperbolic tangent |
Math.asinh(x) |
Inverse hyperbolic sine |
Math.acosh(x) |
Inverse hyperbolic cosine |
Math.atanh(x) |
Inverse hyperbolic tangent |
Random
| Method | Description |
|---|---|
Math.random() |
Float in [0, 1) |
Essential Random Recipes
const rand = (n) => Math.floor(Math.random() * n);
const randInt = (min,max) => Math.floor(Math.random() * (max - min + 1)) + min;
const pick = arr => arr[Math.floor(Math.random() * arr.length)];
const flip = () => Math.random() < 0.5;
// Dice: randInt(1, 6)
// Card: pick(deck)
// Coin: flip()
// Pct: Math.random() < 0.30 → true ~30% of the time
9. Applied Exercises
Phase 2 — Applied Exercises
Exercise 1 — Math Toolkit Builder 🔧
Objective: Practice core Math methods in real calculations.
Scenario: You’re building a geometry calculator for an architecture app.
Warm-up Micro-Demo:
// Area of a circle with radius 7
const area = Math.PI * 7 ** 2;
console.log("Area:", area.toFixed(2)); // 153.94
Task A — Shape Calculator
function shapeCalculator() {
// 1. Circle — area and circumference
const r = 8;
const circleArea = Math.PI * r ** 2;
const circlePerim = 2 * Math.PI * r;
console.log(`Circle (r=${r}):`);
console.log(` Area : ${circleArea.toFixed(4)}`);
console.log(` Circumference: ${circlePerim.toFixed(4)}`);
// 2. Right triangle — hypotenuse
const a = 5, b = 12;
const hyp = Math.hypot(a, b);
console.log(`\nTriangle (a=${a}, b=${b}):`);
console.log(` Hypotenuse : ${hyp.toFixed(4)}`); // 13.0000
// 3. Sphere — volume V = (4/3)πr³
const rSphere = 6;
const sphereVol = (4 / 3) * Math.PI * rSphere ** 3;
console.log(`\nSphere (r=${rSphere}):`);
console.log(` Volume : ${sphereVol.toFixed(4)}`);
// 4. Distance between two 2D points
const p1 = { x: 2, y: 3 }, p2 = { x: 8, y: 11 };
const dist = Math.hypot(p2.x - p1.x, p2.y - p1.y);
console.log(`\nDistance (2,3)→(8,11): ${dist.toFixed(4)}`); // 10.0000
// 5. Rounding comparisons on price
const price = 19.555;
console.log(`\nRounding $${price}:`);
console.log(` round : $${Math.round(price * 100) / 100}`);
console.log(` floor : $${Math.floor(price * 100) / 100}`);
console.log(` ceil : $${Math.ceil(price * 100) / 100}`);
console.log(` trunc : $${Math.trunc(price * 100) / 100}`);
}
shapeCalculator();
Expected Output:
Circle (r=8):
Area : 201.0619
Circumference: 50.2655
Triangle (a=5, b=12):
Hypotenuse : 13.0000
Sphere (r=6):
Volume : 904.7787
Distance (2,3)→(8,11): 10.0000
Rounding $19.555:
round : $19.56
floor : $19.55
ceil : $19.56
trunc : $19.55
Self-check questions:
- Why does
Math.round(19.555 * 100) / 100sometimes give unexpected results? - What is the difference between
Math.floorandMath.truncfor negative numbers? - When would you use
Math.hypot(a, b)instead ofMath.sqrt(a**2 + b**2)?
Exercise 2 — Random Generator Suite 🎲
Objective: Practice all key random patterns — floats, integers, ranges, arrays, weighted.
Scenario: You’re building a test data generator for a school application. All student records need realistic random values.
Warm-up Micro-Demo:
const randInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
const pick = arr => arr[Math.floor(Math.random() * arr.length)];
console.log("Score:", randInt(40, 100)); // e.g. 73
console.log("Grade:", pick(["A","B","C","D","F"])); // e.g. B
Task A — Generate Realistic Student Records
const randInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
const randFloat = (min, max) => +(Math.random() * (max - min) + min).toFixed(2);
const pick = arr => arr[Math.floor(Math.random() * arr.length)];
const shuffle = arr => {
const a = [...arr];
for (let i = a.length - 1; i > 0; i--) {
const j = randInt(0, i);
[a[i], a[j]] = [a[j], a[i]];
}
return a;
};
function generateStudent(id) {
const firstNames = ["Amara","Kwame","Fatima","Emeka","Priya","Omar","Sofia","Yuki","Carlos","Aisha"];
const lastNames = ["Diallo","Asante","Rashid","Obi","Sharma","Hassan","Martini","Tanaka","Ruiz","Nkosi"];
const cities = ["Lagos","Accra","Cairo","Nairobi","Casablanca","Tunis","Dakar"];
const score = randInt(40, 100);
const attendance = randFloat(60, 100);
const grade = score >= 90 ? "A"
: score >= 75 ? "B"
: score >= 60 ? "C"
: score >= 50 ? "D" : "F";
return {
id: `S${String(id).padStart(3, "0")}`,
name: `${pick(firstNames)} ${pick(lastNames)}`,
age: randInt(16, 22),
city: pick(cities),
score,
attendance: `${attendance}%`,
grade,
status: score >= 60 ? "Pass" : "Fail"
};
}
console.log("=".repeat(55));
console.log(" GENERATED STUDENT RECORDS (Sample of 5)");
console.log("=".repeat(55));
const students = Array.from({ length: 5 }, (_, i) => generateStudent(i + 1));
students.forEach(s => {
console.log(`\n ${s.id} | ${s.name}`);
console.log(` Age: ${s.age} City: ${s.city}`);
console.log(` Score: ${s.score} Grade: ${s.grade} (${s.status})`);
console.log(` Attendance: ${s.attendance}`);
});
// Shuffle and pick 2 for a committee
const committee = shuffle(students).slice(0, 2);
console.log(`\nRandomly selected committee:`);
committee.forEach(s => console.log(` → ${s.name} (${s.id})`));
Self-check questions:
- Why does the random integer formula use
Math.floorand notMath.round? - What is the difference between
randomFloat(0, 100)andMath.random() * 100? - How would you ensure no two generated students have the same name?
Exercise 3 — Statistics Calculator 📊
Objective: Use Math methods to compute real statistics on a dataset.
Scenario: You’re building a grade analysis dashboard for a teacher.
Warm-up Micro-Demo:
const scores = [78, 92, 65, 88, 71];
const sum = scores.reduce((a, b) => a + b, 0);
const avg = sum / scores.length;
console.log("Average:", avg.toFixed(1)); // 78.8
Task A — Full Statistical Analysis
function analyse(scores) {
const n = scores.length;
const sorted = [...scores].sort((a, b) => a - b);
const sum = scores.reduce((a, b) => a + b, 0);
const mean = sum / n;
// Median
const mid = Math.floor(n / 2);
const median = n % 2 !== 0
? sorted[mid]
: (sorted[mid - 1] + sorted[mid]) / 2;
// Standard deviation
const variance = scores.reduce((acc, s) => acc + (s - mean) ** 2, 0) / n;
const stdDev = Math.sqrt(variance);
// Range
const max = Math.max(...scores);
const min = Math.min(...scores);
const range = max - min;
// Percentile rank helper
const percentile = (p) => {
const idx = Math.ceil((p / 100) * n) - 1;
return sorted[Math.max(0, idx)];
};
return { n, sum, mean, median, stdDev, max, min, range,
p25: percentile(25), p75: percentile(75) };
}
const classScores = [88, 45, 72, 95, 61, 78, 53, 90, 84, 67, 73, 58, 92, 86, 49];
const stats = analyse(classScores);
console.log("=".repeat(45));
console.log(" CLASS SCORE STATISTICS");
console.log("=".repeat(45));
console.log(`Students : ${stats.n}`);
console.log(`Total : ${stats.sum}`);
console.log(`Mean : ${stats.mean.toFixed(2)}`);
console.log(`Median : ${stats.median}`);
console.log(`Std Dev : ${stats.stdDev.toFixed(2)}`);
console.log(`Min / Max : ${stats.min} / ${stats.max}`);
console.log(`Range : ${stats.range}`);
console.log(`25th pctile : ${stats.p25}`);
console.log(`75th pctile : ${stats.p75}`);
// Distribution by rounding to nearest 10
const distribution = new Map();
classScores.forEach(s => {
const bucket = Math.floor(s / 10) * 10;
distribution.set(bucket, (distribution.get(bucket) || 0) + 1);
});
console.log("\nScore Distribution:");
[...distribution.entries()].sort((a,b) => a[0]-b[0]).forEach(([bucket, count]) => {
console.log(` ${bucket}s: ${"█".repeat(count)} (${count})`);
});
Expected Output:
=============================================
CLASS SCORE STATISTICS
=============================================
Students : 15
Total : 1091
Mean : 72.73
Median : 73
Std Dev : 16.26
Min / Max : 45 / 95
Range : 50
25th pctile : 58
75th pctile : 88
Score Distribution:
40s: ██ (2)
50s: ██ (2)
60s: ██ (2)
70s: ███ (3)
80s: ███ (3)
90s: ███ (3)
10. Mini Project — Casino Dice & Statistics Simulator
Phase 3 — Project Simulation
Real-world scenario: You’re building a browser-based casino simulator to demonstrate probability and statistics. The system simulates dice rolls, tracks outcomes, verifies statistical laws, and generates visual reports — all using Math methods and Math.random().
🔵 Stage 1 — Dice Engine
Goal: Build a fair dice roller for any number of sides, roll multiple dice simultaneously, and compute combined values.
Simple stage preview:
const die = sides => Math.floor(Math.random() * sides) + 1;
console.log("d6:", die(6)); // 1–6
console.log("d20:", die(20)); // 1–20
Stage 1 Full Code:
"use strict";
// Core dice function — roll a single die with `sides` faces
function rollDie(sides = 6) {
return Math.floor(Math.random() * sides) + 1;
}
// Roll multiple dice, return array of results and their sum
function rollDice(count = 2, sides = 6) {
const rolls = Array.from({ length: count }, () => rollDie(sides));
const sum = rolls.reduce((a, b) => a + b, 0);
return { rolls, sum, min: Math.min(...rolls), max: Math.max(...rolls) };
}
// Roll with advantage — roll twice, take the higher (D&D rule)
function rollAdvantage(sides = 20) {
const a = rollDie(sides), b = rollDie(sides);
return { rolls: [a, b], result: Math.max(a, b), type: "advantage" };
}
// Roll with disadvantage — roll twice, take the lower
function rollDisadvantage(sides = 20) {
const a = rollDie(sides), b = rollDie(sides);
return { rolls: [a, b], result: Math.min(a, b), type: "disadvantage" };
}
console.log("=".repeat(50));
console.log(" STAGE 1 — DICE ENGINE");
console.log("=".repeat(50));
const single = rollDie(6);
console.log(`Single d6 : ${single}`);
const triple = rollDice(3, 6);
console.log(`Three d6 : [${triple.rolls.join(", ")}] → Sum: ${triple.sum}`);
const adv = rollAdvantage(20);
console.log(`d20 Advantage : [${adv.rolls.join(", ")}] → Kept: ${adv.result}`);
const dis = rollDisadvantage(20);
console.log(`d20 Disadvantage : [${dis.rolls.join(", ")}] → Kept: ${dis.result}`);
const percentile = rollDice(2, 10); // percentile dice
console.log(`Percentile (2d10): [${percentile.rolls.join(", ")}] → ${percentile.sum - 2 || 100}`);
▶ Expected Output (sample):
==================================================
STAGE 1 — DICE ENGINE
==================================================
Single d6 : 4
Three d6 : [2, 6, 3] → Sum: 11
d20 Advantage : [14, 7] → Kept: 14
d20 Disadvantage : [3, 19] → Kept: 3
Percentile (2d10): [7, 4] → 11
🟢 Stage 2 — Probability Simulator
Goal: Roll dice thousands of times, collect frequency data using a Map, and verify that observed probabilities converge to theoretical probabilities (the Law of Large Numbers).
Simple stage preview:
const freq = new Map();
for (let i = 0; i < 6000; i++) {
const r = rollDie(6);
freq.set(r, (freq.get(r) || 0) + 1);
}
console.log("Expected ~1000 per face:");
for (const [face, count] of [...freq.entries()].sort()) {
console.log(` ${face}: ${count}`);
}
Stage 2 Full Code:
function simulate(trials, sides = 6) {
const freq = new Map();
for (let i = 0; i < trials; i++) {
const roll = rollDie(sides);
freq.set(roll, (freq.get(roll) || 0) + 1);
}
return freq;
}
function printFrequencyReport(freq, trials, sides) {
const theoretical = trials / sides;
const pctLabel = p => (p * 100).toFixed(1) + "%";
console.log(`\nFace Count Observed Expected Deviation Bar`);
console.log("-".repeat(62));
let chiSquare = 0;
for (const face of Array.from({ length: sides }, (_, i) => i + 1)) {
const count = freq.get(face) || 0;
const observed = count / trials;
const expected = 1 / sides;
const dev = Math.abs(observed - expected);
const bar = "█".repeat(Math.round(count / trials * 100));
chiSquare += (count - theoretical) ** 2 / theoretical;
console.log(
` ${String(face).padEnd(4)} ${String(count).padEnd(8)} ` +
`${pctLabel(observed).padEnd(11)}` +
`${pctLabel(expected).padEnd(11)}` +
`${(dev * 100).toFixed(2)}%`.padEnd(11) +
bar
);
}
console.log("-".repeat(62));
console.log(`χ² statistic: ${chiSquare.toFixed(2)} (lower = fairer distribution)`);
console.log(`Std expected : ${((trials / sides) * (1 - 1/sides) / sides * sides).toFixed(2)}`);
}
console.log("\n" + "=".repeat(62));
console.log(" STAGE 2 — PROBABILITY SIMULATION (10,000 rolls)");
console.log("=".repeat(62));
const trials = 10000;
const sides = 6;
const freq = simulate(trials, sides);
console.log(`Simulating ${trials.toLocaleString()} d${sides} rolls...`);
printFrequencyReport(freq, trials, sides);
// Verify Law of Large Numbers — more rolls → closer to theoretical
console.log("\nConvergence Test (deviation from 16.67%):");
[100, 1000, 10000, 100000].forEach(n => {
const f = simulate(n, 6);
const pcts = [...f.values()].map(c => c / n);
const maxDev = Math.max(...pcts.map(p => Math.abs(p - 1/6)));
console.log(` ${String(n).padStart(7)} rolls → max deviation: ${(maxDev * 100).toFixed(2)}%`);
});
▶ Expected Output (sample — varies by randomness):
================================================================
STAGE 2 — PROBABILITY SIMULATION (10,000 rolls)
================================================================
Simulating 10,000 d6 rolls...
Face Count Observed Expected Deviation Bar
--------------------------------------------------------------
1 1662 16.6% 16.7% 0.04% ████████████████
2 1693 16.9% 16.7% 0.23% ████████████████
3 1624 16.2% 16.7% 0.43% ████████████████
4 1698 17.0% 16.7% 0.31% █████████████████
5 1659 16.6% 16.7% 0.09% ████████████████
6 1664 16.6% 16.7% 0.03% ████████████████
--------------------------------------------------------------
χ² statistic: 3.24 (lower = fairer distribution)
Convergence Test (deviation from 16.67%):
100 rolls → max deviation: 3.67%
1000 rolls → max deviation: 1.24%
10000 rolls → max deviation: 0.43%
100000 rolls → max deviation: 0.18%
🟠 Stage 3 — Full Casino Report
Goal: Simulate a full craps-style betting game, compute house edge using Math functions, and generate a complete summary report.
Stage 3 Full Code:
// Craps simplified: roll 2d6
// "Pass line" bet: win on 7 or 11 first roll, lose on 2/3/12, else roll until point or 7
function playCraps() {
const firstRoll = rollDice(2, 6).sum;
// Natural win
if (firstRoll === 7 || firstRoll === 11) return { result: "win", rolls: 1, firstRoll };
// Craps loss
if ([2, 3, 12].includes(firstRoll)) return { result: "loss", rolls: 1, firstRoll };
// Establish point
const point = firstRoll;
let rollCount = 1;
while (true) {
const next = rollDice(2, 6).sum;
rollCount++;
if (next === point) return { result: "win", rolls: rollCount, firstRoll, point };
if (next === 7) return { result: "loss", rolls: rollCount, firstRoll, point };
}
}
function runCasino(sessions) {
let wins = 0, losses = 0, totalRolls = 0;
let maxWinStreak = 0, maxLossStreak = 0;
let curWin = 0, curLoss = 0;
const rollDist = new Map(); // how many rolls did each game last?
for (let i = 0; i < sessions; i++) {
const { result, rolls } = playCraps();
totalRolls += rolls;
rollDist.set(rolls, (rollDist.get(rolls) || 0) + 1);
if (result === "win") {
wins++; curWin++; curLoss = 0;
maxWinStreak = Math.max(maxWinStreak, curWin);
} else {
losses++; curLoss++; curWin = 0;
maxLossStreak = Math.max(maxLossStreak, curLoss);
}
}
const winRate = wins / sessions;
const houseEdge = 1 - winRate * 2; // simplified edge calculation
const avgRolls = totalRolls / sessions;
return { sessions, wins, losses, winRate, houseEdge, avgRolls,
maxWinStreak, maxLossStreak, rollDist };
}
console.log("\n" + "=".repeat(55));
console.log(" STAGE 3 — CASINO CRAPS SIMULATOR");
console.log("=".repeat(55));
const results = runCasino(50000);
console.log(`\nSessions played : ${results.sessions.toLocaleString()}`);
console.log(`Wins : ${results.wins.toLocaleString()} (${(results.winRate*100).toFixed(2)}%)`);
console.log(`Losses : ${results.losses.toLocaleString()}`);
console.log(`House edge : ${(Math.abs(results.houseEdge)*100).toFixed(2)}%`);
console.log(`Avg rolls/game : ${results.avgRolls.toFixed(2)}`);
console.log(`Max win streak : ${results.maxWinStreak}`);
console.log(`Max loss streak : ${results.maxLossStreak}`);
// Theoretical craps win probability is ~49.29% (house edge ~1.41%)
const theoretical = 0.4929;
const deviation = Math.abs(results.winRate - theoretical);
console.log(`\nTheoretical win rate : 49.29%`);
console.log(`Observed win rate : ${(results.winRate * 100).toFixed(2)}%`);
console.log(`Deviation : ${(deviation * 100).toFixed(3)}%`);
console.log(`Result : ${deviation < 0.01 ? "✅ Within expected variance" : "⚠️ Unusually high deviation"}`);
// Roll count distribution (first 8 lengths)
console.log("\nGame Length Distribution:");
[...results.rollDist.entries()]
.sort((a, b) => a[0] - b[0])
.slice(0, 8)
.forEach(([rolls, count]) => {
const pct = (count / results.sessions * 100).toFixed(1);
const bar = "█".repeat(Math.round(count / results.sessions * 80));
console.log(` ${String(rolls).padEnd(3)} rolls: ${pct.padStart(5)}% ${bar}`);
});
console.log("=".repeat(55));
▶ Expected Output (sample):
=======================================================
STAGE 3 — CASINO CRAPS SIMULATOR
=======================================================
Sessions played : 50,000
Wins : 24,638 (49.28%)
Losses : 25,362
House edge : 1.45%
Avg rolls/game : 3.38
Max win streak : 14
Max loss streak : 16
Theoretical win rate : 49.29%
Observed win rate : 49.28%
Deviation : 0.015%
Result : ✅ Within expected variance
Game Length Distribution:
1 rolls: 33.3% ██████████████████████████
2 rolls: 3.8% ███
3 rolls: 7.5% ██████
4 rolls: 8.2% ██████
5 rolls: 8.1% ██████
6 rolls: 7.5% ██████
7 rolls: 6.5% █████
8 rolls: 5.4% ████
Reflection questions:
- Why does the observed win rate converge to the theoretical 49.29% as the number of sessions increases? (Law of Large Numbers)
- Why is
Math.random()sufficient for a casino simulator but NOT for a real online casino? - How would
Math.abs,Math.max, andMath.minhelp you implement a bankroll tracker that shows the peak balance and maximum drawdown? - How would you use
Math.logto model the exponential decay of a player’s bankroll over many losing sessions? - How would you implement a “hot streak” mechanic — where the RNG is secretly biased for a short period — using
Math.random()and a probability threshold?
Optional advanced features:
- Add a bet-sizing strategy (flat, Martingale, Fibonacci) and compare ROI across 10,000 sessions
- Use
Math.pow(winRate, streak)to calculate the theoretical probability of the observed longest win streak - Build a normal distribution random number generator using the Box-Muller transform:
Math.sqrt(-2 * Math.log(Math.random())) * Math.cos(2 * Math.PI * Math.random()) - Generate a histogram of 2d6 sums (showing the classic bell curve of 7 being most common)
11. Completion Checklist
- ✅ I know that
Mathis a static object — never usenew Math(). - ✅ I can recall all 8 Math constants:
E,PI,SQRT2,SQRT1_2,LN2,LN10,LOG2E,LOG10E. - ✅ I can use
Math.PIandMath.Ein real formulas (circle area, compound interest). - ✅ I know all four rounding methods —
round(nearest),floor(toward -∞),ceil(toward +∞),trunc(toward 0) — and their behaviour on negative numbers. - ✅ I know that
Math.round(4.5)→5butMath.round(-4.5)→-4(rounds toward zero for .5). - ✅ I can use
Math.abs()for distance/difference calculations,Math.sqrt()for Pythagorean distances, andMath.hypot()as the cleaner alternative. - ✅ I understand
Math.pow(x, y)and its modern**operator equivalent. - ✅ I can use
Math.sign()to determine direction (-1/0/1) andMath.cbrt()for cube roots. - ✅ I can find the largest and smallest values in an array using
Math.max(...arr)andMath.min(...arr). - ✅ I can implement a
clamp(value, min, max)function usingMath.minandMath.max. - ✅ I know the key logarithm methods:
Math.log()(base e),Math.log2(),Math.log10(). - ✅ I understand that trig functions use radians, and can convert:
deg × (π/180)andrad × (180/π). - ✅ I can use
Math.atan2(y, x)to find the angle between two points. - ✅ I know that
Math.random()returns[0, 1)— includes 0, excludes 1. - ✅ I can generate random integers using the correct formula:
Math.floor(Math.random() * (max - min + 1)) + min. - ✅ I know NOT to use
Math.roundfor random integers (unequal distribution at boundaries). - ✅ I can pick a random element from an array:
arr[Math.floor(Math.random() * arr.length)]. - ✅ I can implement Fisher-Yates shuffle for uniform random array permutations.
- ✅ I understand weighted random selection for non-uniform probability distributions.
- ✅ I know that
Math.random()is pseudo-random — NOT suitable for cryptographic uses (crypto.getRandomValues()is needed instead). - ✅ I completed all three exercises and the full three-stage mini project.
📌 One-Sentence Summary of Each Topic
Math Object: The Math object is a built-in static namespace (never instantiated with new) providing 8 mathematical constants and 30+ methods for rounding, arithmetic, exponentiation, logarithms, and trigonometry.
Math Properties: JavaScript’s 8 Math constants provide pre-computed precision values for Euler’s number (Math.E), Pi (Math.PI), square roots, and logarithmic bases — eliminating the need to hardcode these values.
Rounding: JavaScript provides four rounding methods — round (nearest), floor (toward -∞), ceil (toward +∞), and trunc (toward zero) — whose differences matter most for negative numbers and financial precision.
Arithmetic & Exponents: Core math functions including abs, sqrt, cbrt, pow, hypot, sign, and bitwise helpers enable everything from geometric distance calculations to 32-bit integer arithmetic.
Min, Max & Clamp: Math.max(...arr) and Math.min(...arr) find extremes in any set of values, and combining them into a clamp(value, min, max) utility is one of the most widely used patterns in games, UIs, and data processing.
Logarithms & Trigonometry: Logarithm methods (log, log2, log10) power data science and bit calculations, while trigonometry methods work in radians and are essential for animations, maps, and physics simulations — with Math.atan2 being the key direction-finding tool.
Math.random(): Math.random() returns a pseudo-random float in [0, 1) — combine it with Math.floor and range arithmetic to generate integers, pick array elements, shuffle lists, and implement weighted probability — but use crypto.getRandomValues() for any security-sensitive randomness.
📘 Built from W3Schools.com —
js_math|js_math_reference|js_randomFramework: Understand → Practice → Create