Created
April 28, 2018 04:09
-
-
Save KJLJon/75b44a0e60e69da4c43b2bcbefa60f17 to your computer and use it in GitHub Desktop.
Xincrol algorithm example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Xincrol from './Xincrol'; | |
class Deck { | |
constructor(...rules) { | |
this.setRules(rules); | |
this.size = rules.reduce((a, b) => { | |
const base = typeof a === 'string' ? a.length : a; | |
return base * b.length; | |
}); | |
this.shuffle(); | |
} | |
setRules(rules) { | |
let possibilities = 1; | |
this.rules = rules.map((value) => { | |
const results = { | |
range: possibilities, | |
value, | |
}; | |
possibilities *= value.length; | |
return results; | |
}); | |
} | |
shuffle() { | |
// http://openpatent.blogspot.co.il/2013/04/xincrol-unique-and-random-number.html | |
this.seq = new Xincrol(this.size); | |
} | |
getNextCardIndex() { | |
return this.seq.next(); | |
// return Math.floor(Math.random() * this.size); | |
} | |
pickCard() { | |
let cardNumber = this.getNextCardIndex(); | |
let card = ''; | |
for (let j = this.rules.length - 1; j >= 0; j -= 1) { | |
const rule = this.rules[j]; | |
const index = Math.floor(cardNumber / rule.range); | |
cardNumber -= index * rule.range; | |
card = rule.value[index] + card; | |
} | |
return card; | |
} | |
/** | |
* Exception for cards | |
* @param string message | |
* @param string[] cards | |
*/ | |
createCardException = (message, cards) => ({ | |
message, | |
cards, | |
}); | |
/** | |
* Deals from the deck | |
* @param integer numberOfCards | |
*/ | |
deal(numberOfCards) { | |
const cards = []; | |
for (let i = 0; i < numberOfCards; i += 1) { | |
const card = this.pickCard(); | |
if (typeof card === 'undefined') { | |
throw this.createCardException('Not enough cards in the deck', cards); | |
} | |
cards.push(card); | |
} | |
return cards; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Deck from './Deck'; | |
const deck = new Deck('dsch', '23456789TJQKA'); | |
//deal will repeat cards if numberOfCards > total number of available cards | |
const numberOfCards = 20; | |
const cards = deck.deal(numberOfCards); | |
console.log(cards); | |
//use deck.shuffle() if you want to shuffle the deck |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//quick and dirty port of http://openpatent.blogspot.com/2013/04/xincrol-unique-and-random-number.html | |
/** | |
* Xincrol - Unique and Random Number Generator | |
* Xincrol.java | |
* Purpose: Generating unique random numbers in specified range. | |
* | |
* @author Tofig Kareemov | |
* @version 1.7 2013.05.03 | |
* | |
* v1.7 notes: improved randomness for big numbers | |
* | |
* v1.6 notes: made ROL and ROR functions to support big masks | |
* | |
* v1.5 notes: Fixed bug in ROL and ROR functions | |
* | |
* v1.4 notes: added REFLECT and NOT methods | |
*/ | |
export default class Xincrol { | |
iUniqueSeed = 0; | |
iKey = new Array(31).fill(null); | |
iBaseRange = 0; | |
iBase = 0; | |
iBaseMask = 0; | |
iBaseBitSize = 0; | |
constructor(iRange) { | |
this.randomize(iRange); | |
} | |
// Private Methods... | |
reset = (bNew) => { | |
this.iUniqueSeed = 0; | |
// todo find other way to generate | |
this.hashKey(navigator.userAgent, bNew); | |
this.hashKey(`${Date.now()}`, false); | |
for (let i = 0; i < this.iKey.length; ++i) { | |
const date = new Date(); | |
this.hashKey(`${date.getMilliseconds()}`, false); | |
} | |
} | |
hashKey = (sKey, bNew) => { | |
if ((sKey == null) || (sKey === '')) { | |
return; | |
} | |
if (bNew) { | |
for (let i = 0; i < this.iKey.length; ++i) { | |
this.iKeyValue = 0; | |
for (let i1 = 0; i1 < 4; ++i1) { | |
const iBit = (i >>> i1) & 1; | |
if (iBit == 1) { | |
this.iKeyValue ^= 0x5a; | |
} else { | |
this.iKeyValue ^= 0xa5; | |
} | |
if (i1 < 3) { | |
this.iKeyValue <<= 8; | |
} | |
} | |
this.iKey[i] = this.iKeyValue; | |
} | |
} | |
let iGlue = this.iKey[this.iKey.length - 1]; | |
this.iKeyIndex = 0; | |
for (let i1 = 0; i1 < this.iKey.length; ++i1) { | |
for (let i = 0; i < sKey.length; ++i) { | |
this.iKey[this.iKeyIndex] ^= sKey[i]; | |
this.iKey[this.iKeyIndex] = (this.iKey[this.iKeyIndex] << 1) | (this.iKey[this.iKeyIndex] >>> 31); | |
this.iKey[this.iKeyIndex] ^= iGlue; | |
this.iKey[this.iKeyIndex] = (this.iKey[this.iKeyIndex] << 1) | (this.iKey[this.iKeyIndex] >>> 31); | |
iGlue = this.iKey[this.iKeyIndex]; | |
this.iKeyIndex = (++this.iKeyIndex) % this.iKey.length; | |
} | |
} | |
} | |
// Main Function | |
XOR = (iA, iB) => ((iA ^ iB) & this.iBaseMask); | |
// INC(var iA) { | |
// return ((++iA) & this.iBaseMask); | |
// } | |
ADD = (iA, iB) => ((iA + iB) & this.iBaseMask); | |
ROL = (iA, iPowerDistance) => { | |
if (this.iBaseBitSize <= 0) { | |
return iA; | |
} | |
iPowerDistance %= this.iBaseBitSize; | |
this.iBaseBit = ((this.iBaseMask >>> 1) + 1); | |
for (let i = 0; i < iPowerDistance; ++i) { | |
if ((iA & this.iBaseBit) != 0) { | |
iA = ((iA << 1) & this.iBaseMask) | 1; | |
} else { | |
iA = ((iA << 1) & this.iBaseMask); | |
} | |
} | |
return (iA); | |
} | |
REFLECT(iA) { | |
let iB = 0; | |
this.iBaseBit = ((this.iBaseMask >>> 1) + 1); | |
for (; (iA > 0) && (this.iBaseBit > 0);) { | |
if ((iA & 1) != 0) { | |
iB |= this.iBaseBit; | |
} | |
this.iBaseBit >>>= 1; | |
iA >>>= 1; | |
} | |
return iB; | |
} | |
generate(bUp) { | |
let iResult = this.iBaseRange; | |
for (let i = 0; (i < this.iBase) && (iResult >= this.iBaseRange); ++i) { | |
if (bUp) { | |
this.iUniqueSeed = (++this.iUniqueSeed) % this.iBase; | |
} else { | |
this.iUniqueSeed = (--this.iUniqueSeed) % this.iBase; | |
} | |
iResult = this.iUniqueSeed; | |
for (let i1 = 0; i1 < this.iKey.length; ++i1) { | |
iResult = this.ROL(iResult, 1); | |
let iCommand = this.iKey[i1]; | |
for (let i2 = 0; i2 < this.iBaseBitSize; ++i2) { | |
const iOperand = (iCommand + i1 + i2); | |
switch (iCommand & 3) { | |
case 0: | |
iResult = this.REFLECT(iResult); | |
break; | |
case 1: | |
iResult = this.ROL(iResult, iOperand); | |
break; | |
case 2: | |
iResult = this.ADD(iResult, iOperand); | |
break; | |
case 3: | |
iResult = this.XOR(iResult, iOperand); | |
break; | |
} | |
iCommand >>>= 1; | |
} | |
} | |
} | |
return iResult; | |
} | |
// Public Methods | |
getRange = () => this.iBaseRange; | |
randomize = (iRange) => { | |
this.iBaseRange = iRange; | |
this.iBase = 1; | |
if (this.iBaseRange <= 0) { | |
this.iBaseRange = 0; | |
return; | |
} | |
if (this.iBaseRange > Number.MAX_SAFE_INTEGER / 2) { | |
this.iBaseRange = Number.MAX_SAFE_INTEGER / 2; | |
} | |
for (this.iBaseBitSize = 0; this.iBase < this.iBaseRange; this.iBase <<= 1) { | |
++this.iBaseBitSize; | |
} | |
if (this.iBaseBitSize > 1) { | |
--this.iBaseBitSize; | |
} | |
this.iBaseMask = this.iBase - 1; | |
if ((this.iKey == null) || (this.iUniqueSeed >= this.iBase)) { | |
this.reset(false); | |
} | |
this.reset(false); | |
} | |
setKey = (sKey, iRange) => { | |
this.iBaseRange = iRange; | |
this.setKey(sKey); | |
} | |
setKey = (sKey) => { | |
this.iBase = 1; | |
if (this.iBaseRange <= 0) { | |
this.iBaseRange = 0; | |
return; | |
} | |
if (this.iBaseRange > Number.MAX_SAFE_INTEGER / 2) { | |
this.iBaseRange = Number.MAX_SAFE_INTEGER / 2; | |
} | |
for (this.iBaseBitSize = 0; this.iBase < this.iBaseRange; this.iBase <<= 1) { | |
++this.iBaseBitSize; | |
} | |
if (this.iBaseBitSize > 0) { | |
--this.iBaseBitSize; | |
} | |
this.iBaseMask = this.iBase - 1; | |
if ((this.iKey == null) || (this.iUniqueSeed >= this.iBase)) { | |
this.reset(false); | |
} | |
this.iUniqueSeed = 0; | |
this.hashKey(sKey, true); | |
} | |
next = () => this.generate(true); | |
prev = () => this.generate(false); | |
encode = (iInput) => { | |
let iResult = (iInput + this.generate(true)); | |
if (iResult >= this.iBaseRange) { | |
iResult -= this.iBaseRange; | |
} | |
this.hashKey(`${iInput}`, false); | |
return iResult; | |
} | |
decode = (iInput) => { | |
let iResult = (iInput - this.generate(true)); | |
if (iResult < 0) { | |
iResult += this.iBaseRange; | |
} | |
this.hashKey(`${iResult}`, false); | |
return iResult; | |
} | |
random = (iRange) => { | |
this.iBase = 1; | |
if (iRange <= 0) { | |
return 0; | |
} | |
for (; (this.iBase < iRange) && (this.iBase < Number.MAX_SAFE_INTEGER / 2); this.iBase <<= 1) { | |
} | |
this.randomize(iRange); | |
return (this.iKey[this.iKey.length - 1] & (this.iBase - 1)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment