From 8a8d7ddb512380181f521c7d1e417ca7d48ff9bf Mon Sep 17 00:00:00 2001 From: Dominykas Mostauskis <2715476+dmos62@users.noreply.github.com> Date: Tue, 25 Jun 2024 18:49:36 +0300 Subject: [PATCH 1/4] Add seedability Implements a custom random number generator that accepts a seed and adds a seed parameter to the `generateRandomAvatarData` function. --- src/index.js | 48 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/index.js b/src/index.js index e65c955..6645baa 100644 --- a/src/index.js +++ b/src/index.js @@ -39,16 +39,56 @@ function parseAvatarData(data, separator) { return ret; } -export function generateRandomAvatarData(complexity = 16, avatarDataSeparator = '-') { - const xAxis = Math.floor(Math.random() * Math.pow(2, complexity - 1)); - const yAxis = Math.floor(Math.random() * (Math.pow(2, complexity) - 1)) + 1; +// https://stackoverflow.com/a/47593316/1714997 +function cyrb128(str) { + let h1 = 1779033703, h2 = 3144134277, + h3 = 1013904242, h4 = 2773480762; + for (let i = 0, k; i < str.length; i++) { + k = str.charCodeAt(i); + h1 = h2 ^ Math.imul(h1 ^ k, 597399067); + h2 = h3 ^ Math.imul(h2 ^ k, 2869860233); + h3 = h4 ^ Math.imul(h3 ^ k, 951274213); + h4 = h1 ^ Math.imul(h4 ^ k, 2716044179); + } + h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067); + h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233); + h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213); + h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179); + h1 ^= (h2 ^ h3 ^ h4), h2 ^= h1, h3 ^= h1, h4 ^= h1; + return [h1>>>0, h2>>>0, h3>>>0, h4>>>0]; +} + +// https://stackoverflow.com/a/47593316/1714997 +function sfc32(a, b, c, d) { + return function() { + a |= 0; b |= 0; c |= 0; d |= 0; + let t = (a + b | 0) + d | 0; + d = d + 1 | 0; + a = b ^ b >>> 9; + b = c + (c << 3) | 0; + c = (c << 21 | c >>> 11); + c = c + t | 0; + return (t >>> 0) / 4294967296; + } +} + +function getRandomNumberGenerator(seed) { + const seedString = String(seed); // Making sure that the seed is a string. + const seed = cyrb128(seedString); + return sfc32(seed[0], seed[1], seed[2], seed[3]); +} + +export function generateRandomAvatarData(complexity = 16, avatarDataSeparator = '-', seed = Math.random()) { + const getRandomNumber = getRandomNumberGenerator(seed); + const xAxis = Math.floor(getRandomNumber() * Math.pow(2, complexity - 1)); + const yAxis = Math.floor(getRandomNumber() * (Math.pow(2, complexity) - 1)) + 1; const rows = getBinaryList(yAxis, complexity); let ret = `${xAxis.toString(36)}${avatarDataSeparator}${yAxis.toString(36)}`; let color; rows.forEach(() => { - color = Math.floor(Math.random() * 16777215); + color = Math.floor(getRandomNumber() * 16777215); ret += `${avatarDataSeparator}${color.toString(36)}`; }); From cf208d10a76e78f8586a8dedcab24ff6e9150869 Mon Sep 17 00:00:00 2001 From: Dominykas Mostauskis <2715476+dmos62@users.noreply.github.com> Date: Tue, 25 Jun 2024 18:55:56 +0300 Subject: [PATCH 2/4] Add tests for seed functionality --- src/index.test.js | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/index.test.js b/src/index.test.js index 3245aa6..b68adad 100644 --- a/src/index.test.js +++ b/src/index.test.js @@ -1,6 +1,6 @@ import { getRandomAvatar, getAvatarFromData, generateRandomAvatarData } from './index.js'; -describe('Generate random avatar Ddta', () => { +describe('Generate random avatar Data', () => { it('should return a random code from default values', () => { const avatarCode = generateRandomAvatarData(); expect(avatarCode.split('-').length).toEqual(18); @@ -11,6 +11,27 @@ describe('Generate random avatar Ddta', () => { }); }); +describe('Generate random avatar data with seed', () => { + it('should return the same code for the same seed', () => { + const seed = "apples"; + const avatarCode1 = generateRandomAvatarData(16, '-', seed); + const avatarCode2 = generateRandomAvatarData(16, '-', seed); + expect(avatarCode1).toEqual(avatarCode2); + }); + it('should return different codes for different seeds', () => { + const seed1 = "apples"; + const seed2 = "oranges"; + const avatarCode1 = generateRandomAvatarData(16, '-', seed1); + const avatarCode2 = generateRandomAvatarData(16, '-', seed2); + expect(avatarCode1).not.toEqual(avatarCode2); + }); + it('should return different codes when no seed is provided', () => { + const avatarCode1 = generateRandomAvatarData(); + const avatarCode2 = generateRandomAvatarData(); + expect(avatarCode1).not.toEqual(avatarCode2); + }); +}); + describe('Get svg from avatar data', () => { it('should fail if wrong avatar code is submited', () => { expect(() => getAvatarFromData('')).toThrow('Incorrect avatar data'); @@ -41,4 +62,4 @@ describe('Get a random svg avatar', () => { expect(getRandomAvatar(2, 'circle')).toMatch(/.+<\/svg>/); expect(getRandomAvatar(2, 'circle', 16)).toMatch(/.+<\/svg>/); }); -}); \ No newline at end of file +}); From 67846b479d8fc528228229e2016d72bbe933a592 Mon Sep 17 00:00:00 2001 From: Dominykas Mostauskis <2715476+dmos62@users.noreply.github.com> Date: Tue, 25 Jun 2024 18:59:19 +0300 Subject: [PATCH 3/4] Update index.d.ts --- src/index.d.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/index.d.ts b/src/index.d.ts index f40a6c6..7e67b6e 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -16,9 +16,10 @@ type RenderFunction = (resolution: number, indexX: number, indexY: number) => st * Returns a randomly generated string representation code of an avatar for a given `complexity` * @param {number} [complexity] - A positive integer that represents the number of rows and columns to be drawn * @param {string} [avatarDataSeparator] - A character to be used as data separator + * @param {any} [seed] - A value to be used as the seed for generating the avatar data; accepts any value that the `String` constructor would accept * @return {string} Output example: 0-6-6te25-9d9p0-xd5g */ -export function generateRandomAvatarData(complexity?: number, avatarDataSeparator?: string): string; +export function generateRandomAvatarData(complexity?: number, avatarDataSeparator?: string, seed?: any): string; /** * Returns a string with a valid SVG markup for a given `avatarData` From a33e1700fc035a6741859ba22ccc15b82dc10692 Mon Sep 17 00:00:00 2001 From: Dominykas Mostauskis Date: Tue, 25 Jun 2024 17:27:42 +0000 Subject: [PATCH 4/4] Fix variable names --- src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index 6645baa..8f10335 100644 --- a/src/index.js +++ b/src/index.js @@ -74,8 +74,8 @@ function sfc32(a, b, c, d) { function getRandomNumberGenerator(seed) { const seedString = String(seed); // Making sure that the seed is a string. - const seed = cyrb128(seedString); - return sfc32(seed[0], seed[1], seed[2], seed[3]); + const seedHashed = cyrb128(seedString); + return sfc32(seedHashed[0], seedHashed[1], seedHashed[2], seedHashed[3]); } export function generateRandomAvatarData(complexity = 16, avatarDataSeparator = '-', seed = Math.random()) {