Add more color variations
This commit is contained in:
parent
3655d4d456
commit
d6fa339755
2 changed files with 152 additions and 14 deletions
|
|
@ -3,6 +3,8 @@ import {
|
|||
calculateBasePoints,
|
||||
calculatePointsWithBreakdown,
|
||||
getPlayerRank,
|
||||
generatePlayerColor,
|
||||
PLAYER_COLORS,
|
||||
POINTS_PER_QUESTION,
|
||||
QUESTION_TIME_MS,
|
||||
} from '../constants';
|
||||
|
|
@ -563,3 +565,143 @@ describe('getPlayerRank', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('generatePlayerColor', () => {
|
||||
describe('output format', () => {
|
||||
it('returns valid HSL format', () => {
|
||||
const color = generatePlayerColor(0);
|
||||
expect(color).toMatch(/^hsl\(\d+, \d+%, \d+%\)$/);
|
||||
});
|
||||
|
||||
it('returns valid HSL values within correct ranges', () => {
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const color = generatePlayerColor(i);
|
||||
const match = color.match(/^hsl\((\d+), (\d+)%, (\d+)%\)$/);
|
||||
|
||||
expect(match).not.toBeNull();
|
||||
const [, hue, saturation, lightness] = match!.map(Number);
|
||||
|
||||
expect(hue).toBeGreaterThanOrEqual(0);
|
||||
expect(hue).toBeLessThan(360);
|
||||
expect(saturation).toBeGreaterThanOrEqual(70);
|
||||
expect(saturation).toBeLessThanOrEqual(90);
|
||||
expect(lightness).toBeGreaterThanOrEqual(45);
|
||||
expect(lightness).toBeLessThanOrEqual(55);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('determinism', () => {
|
||||
it('returns same color for same index', () => {
|
||||
const color1 = generatePlayerColor(5);
|
||||
const color2 = generatePlayerColor(5);
|
||||
expect(color1).toBe(color2);
|
||||
});
|
||||
|
||||
it('returns consistent colors across multiple calls', () => {
|
||||
const colors = Array.from({ length: 10 }, (_, i) => generatePlayerColor(i));
|
||||
const colorsAgain = Array.from({ length: 10 }, (_, i) => generatePlayerColor(i));
|
||||
expect(colors).toEqual(colorsAgain);
|
||||
});
|
||||
});
|
||||
|
||||
describe('color distribution (golden ratio)', () => {
|
||||
it('generates distinct hues for consecutive indices (golden ratio creates ~137° separation)', () => {
|
||||
const hues: number[] = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const color = generatePlayerColor(i);
|
||||
const match = color.match(/^hsl\((\d+),/);
|
||||
hues.push(Number(match![1]));
|
||||
}
|
||||
|
||||
for (let i = 1; i < hues.length; i++) {
|
||||
const diff = Math.abs(hues[i] - hues[i - 1]);
|
||||
const wrappedDiff = Math.min(diff, 360 - diff);
|
||||
expect(wrappedDiff).toBeGreaterThan(30);
|
||||
}
|
||||
});
|
||||
|
||||
it('generates well-distributed colors for large player counts', () => {
|
||||
const hues = new Set<number>();
|
||||
for (let i = 0; i < 50; i++) {
|
||||
const color = generatePlayerColor(i);
|
||||
const match = color.match(/^hsl\((\d+),/);
|
||||
hues.add(Number(match![1]));
|
||||
}
|
||||
|
||||
expect(hues.size).toBeGreaterThan(30);
|
||||
});
|
||||
|
||||
it('produces 3 saturation levels (70, 80, 90)', () => {
|
||||
const saturations = new Set<number>();
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const color = generatePlayerColor(i);
|
||||
const match = color.match(/, (\d+)%,/);
|
||||
saturations.add(Number(match![1]));
|
||||
}
|
||||
|
||||
expect(saturations.size).toBe(3);
|
||||
});
|
||||
|
||||
it('produces 2 lightness levels (45, 55)', () => {
|
||||
const lightnesses = new Set<number>();
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const color = generatePlayerColor(i);
|
||||
const match = color.match(/, (\d+)%\)$/);
|
||||
lightnesses.add(Number(match![1]));
|
||||
}
|
||||
|
||||
expect(lightnesses.size).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('handles index 0', () => {
|
||||
const color = generatePlayerColor(0);
|
||||
expect(color).toMatch(/^hsl\(\d+, \d+%, \d+%\)$/);
|
||||
});
|
||||
|
||||
it('handles large indices', () => {
|
||||
const color = generatePlayerColor(1000);
|
||||
expect(color).toMatch(/^hsl\(\d+, \d+%, \d+%\)$/);
|
||||
|
||||
const match = color.match(/^hsl\((\d+), (\d+)%, (\d+)%\)$/);
|
||||
const [, hue, sat, light] = match!.map(Number);
|
||||
expect(hue).toBeLessThan(360);
|
||||
expect(sat).toBeLessThanOrEqual(90);
|
||||
expect(light).toBeLessThanOrEqual(55);
|
||||
});
|
||||
|
||||
it('handles negative indices gracefully', () => {
|
||||
const color = generatePlayerColor(-1);
|
||||
expect(color).toMatch(/^hsl\(-?\d+, \d+%, \d+%\)$/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('PLAYER_COLORS', () => {
|
||||
it('contains exactly 50 colors', () => {
|
||||
expect(PLAYER_COLORS).toHaveLength(50);
|
||||
});
|
||||
|
||||
it('all colors are valid HSL strings', () => {
|
||||
for (const color of PLAYER_COLORS) {
|
||||
expect(color).toMatch(/^hsl\(\d+, \d+%, \d+%\)$/);
|
||||
}
|
||||
});
|
||||
|
||||
it('first color matches generatePlayerColor(0)', () => {
|
||||
expect(PLAYER_COLORS[0]).toBe(generatePlayerColor(0));
|
||||
});
|
||||
|
||||
it('colors are generated in order', () => {
|
||||
for (let i = 0; i < PLAYER_COLORS.length; i++) {
|
||||
expect(PLAYER_COLORS[i]).toBe(generatePlayerColor(i));
|
||||
}
|
||||
});
|
||||
|
||||
it('has 20 unique colors for first 20 indices (typical game size)', () => {
|
||||
const uniqueColors = new Set(PLAYER_COLORS.slice(0, 20));
|
||||
expect(uniqueColors.size).toBe(20);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue