i am making a multiplayer game using html , node js and socket.io. Initially i was sending socket_id, player positions(x,y) and angle in game updates. As socket id is quite long and uses more bytes , i want to use a small (2 or 3 words/number) id to represent each player in place of socket id. Max a game can have 50 players. If i make id using random numbers(0-100) there are chances that id is already taken by some player(if there are already 30 to 40 players). What could be the better algorithm to assign id.
When a player dies or quit the game, that id is free so that it can be assigned(not necessarily) to new player.
One way is to simply increment:
let curId = 0;
//...
player.id = curId;
curId+=1;
If you want to make it random, follow this:
Make an array of numbers 0..100
Pick a random number for the ID from the array
Remove it from the array
Repeat
I would recommend the first approach because the second one limits the number of potential users. Though theoretically, if I had 10,000 known users, I would use the second approach and add the id back to the array when the user disconnected. So I didn't keep incrementing indefinitely.
Just save all used id's in an array and then check if id is unique:
const arrayOfIds = [];
const getRandomBetween1And60 = () => {
let newNumber = arrayOfIds[0] // just as start value
while(arrayOfIds.includes(newNumber)) {
newNumber = Math.floor(Math.random() * 60) + 1
}
return newNumber;
}
...
const newId = getRandomBetween1And60();
Related
Im just trying to insert a number of values into Firebase, for analysis reasons. In fact, im testing the database for a university project. The idea was to get the time it takes to insert and retrieve data.
The code im doing is simple, but it wont insert into the database the number of messages i want.
function sendMessage(times){
for(let i = 0; i < times; i++){
console.log(i); // this prints 50 times
let messageObj = {
user: "testUser" + i,
message: "message" + i
};
firebase.database().ref("History/" + Date.now()).set(messageObj); // this executes only a random number of times
}
window.onload = function () {
sendMessage(50);
}
Here is an image of the data inserted into Firebase Realtime Database.
As you can see, the data is being inserted in a random way. Why is this happening? Thanks in advance.
Using Date.now() for your node name is going to lead to multiple nodes ending up on the same millisecond. And since keys in Firebase are unique by definition, that means that you're overwriting the previous result on the same timestamp.
A slightly better approach is to also embed the value of i in the key, which ensures they are always unique:
firebase.database().ref("History/" + i + "_" + Date.now()).set(messageObj)
Even better would be to use Firebase's built-in push operations, which prevents this type of problem (and many others) entirely:
firebase.database().ref("History/").push(messageObj)
How can I create a random 16 digit number that doesn't previously exist in a list?
Let's say I have the following list:
const nums = [7856328870763265, 0107654389657487];
I need a function to generate random 16 digit numbers but that doesn't create duplicates.
I have the following:
const val = Math.floor(1000 + Math.random() * 9000).toString();
const random16digitnumber = +val+val+val+val;
console.log(random16digitnumber);
console.log(typeof random16digitnumber);
But im afraid at some point it will start creating duplicates. I'm using the random16digitnumber as a primary key in a SQL database for a small school project.
So going back to my example, how can I create a random 16 digit number that doesn't previously exist in an array?
I don't know much about JavaScript but I'm going to give you a general solution.
You need to use a loop statement to loop through the list to check whether the generated number is in the array. If it is not in the array then append/push it to the list. Else regenerate the random number.
Could have written it in php or python
I'll answer with an inline comment. I left your algo "as is". There might be reasons of why you're doing it this way; so I just implemented the logic you've wished for.
const nums = [7856328870763265, 0107654389657487];
const getNextRandomNumber = () => {
const val = Math.floor(1000 + Math.random() * 9000).toString();
const candidate = +val+val+val+val;
// the generated number is part of the list;
// recursively call this function to generate another one
// (this works like a loop, just using call recursion)
// until a number is found that is not part of the list
// then the return below this if() is triggered
// and the "good" candidate is returned
if (nums[candidate]) {
return getNextRandomNumber();
}
// parseInt makes sure the string is converted back to number
// making sure we're using base 10, even if the first digit
// might be 0
return parseInt(candidate, 10);
}
// push() adds an element to the array
// the return value (100% guaranteed to be collision free random number)
// is added via the function call. The function logic makes sure
// it is absolutely unique
nums.push(getNextRandomNumber());
If you want true randomness without duplicates, then you will need to every time check the list to ensure that it's not been included. Eventually that could become a drag on the system, but there's no alternative.
If you want a small enough chance of duplicates appearing that your code can likely ignore it (unless you have phenomenal volume), then GUIDs are probably your best bet... but these are far longer than 16 digits.
However, if you want numbers that appear at a casual glance to be random, although they are far from it, then a simple algorithm will give you all 10^16 numbers in a random-looking order. We can achieve this by taking subsequent numbers as a linear transform of the current ones. We can multiply our current value by some number, a and add another value, b, then take the last 16 digits to get the next value. So long as a and b have no common proper factors and neither has any common proper factors with 10^16, this process will cycle through all numbers from zero to 9999999999999999 in a seemingly random order.
The easiest way to ensure there are no common factors is just to choose two 16-digit primes, which we can do at a site like https://bigprimes.org/.
If we can keep hold of or easily look up the last-used id, then we can write it like this:
const a = BigInt ( '7791448648907857')
const b = BigInt ( '2320712743817497')
const c = BigInt ('10000000000000000')
const nextId = (previousId) =>
((a * BigInt(previousId) + b) % c)
.toString () .padStart ('0', 16)
const lastRecord = {id: '1234567890123456', more: 'fields'}
console .log (nextId (lastRecord .id))
If we can't easily keep track of the last id, but can keep a sequential counter, say n, we can always generate it using the formula
id_n = (a ** n + b * ((a ** n - 1) / (a - 1))) % c
That would involve efficiently managing modular exponentiation, but that's a well-solved problem and is easy enough to implement.
Don't forget that this is no only not cryptographically secure, it's far far from random. But it will give you random-looking number without repetitions.
I'm developing a app and need get a random user from my firebase user list. Whenever a user registers, the system updates the user count on an especific node. So, I draw a number from 1 to the total user. And now, how do I select a user based on that number?
Assuming all of your users are stored in a /users node with keys of their uid and assuming the uid's are ordered (which they always are), there are several options.
1) Load all of the users from the /users node into an array and select the one you want via it's index. Suppose we want the 4th user:
let usersRef = self.ref.child("users")
usersRef.observeSingleEvent(of: .value, with: { snapshot in
let allUsersArray = snapshot.children.allObjects
let thisUserSnap = allUsersArray[3]
print(thisUserSnap)
})
While this works for a small amount of users, it could overwhelm the device if you have say, 10,000 users and lots of data stored in each node.
2) Create a separate node to just store the uid's. This is a significantly smaller dataset and would work the same way as 1)
uids
uid_0: true
uid_1: true
uid_2: true
uid_3: true
uid_4: true
uid_5: true
uid_6: true
uid_7: true
3) Reduce the size of your dataset further. Since you know how many users you have, split the dataset up into two sections and work with that.
using the same structure as 2)
let uidNode = self.ref.child("uids")
let index = 4 //the node we want
let totalNodeCount = 8 //the total amount of uid's
let mid = totalNodeCount / 2 //the middle node
if index <= mid { //if the node we want is in the first 1/2 of the list
print("search first section")
let q = uidNode.queryLimited(toFirst: UInt(index) )
q.observeSingleEvent(of: .value, with: { snapshot in
let array = snapshot.children.allObjects
print(array.last) //the object we want will be the last one loaded
})
} else {
print("search second section")
let q = uidNode.queryLimited(toLast: UInt(index) )
q.observeSingleEvent(of: .value, with: { snapshot in
let array = snapshot.children.allObjects
print(array.first) //the object we want will be the first one loaded
})
}
this method only returns 1/2 of the list so it's a much more manageable amount of data.
If you are talking about your authenticated users, the only way to retreive a list of them is by calling the corresponding admin function and applying your logic to it afterwards.
Another way could be writing a trigger for your authentication and store the userId with an incrementing number (and maybe save a totalUser field), then you only need to generate a random number and access said user.
I am trying to generate a unique random string.
var string = Math.floor( Math.random() * 1000 ) + Date.now();
The above method which I am using right now gives me too long a string and I want a shorter unique one.
Here is a workaround to avoid the issue of repeated ids:
var GenRandom = {
Stored: [],
Job: function(){
var newId = Date.now().toString().substr(6); // or use any method that you want to achieve this string
if( this.Check(newId) ){
this.Job();
}
this.Stored.push(newId);
return newId; // or store it in sql database or whatever you want
},
Check: function(id){
for( var i = 0; i < this.Stored.length; i++ ){
if( this.Stored[i] == id ) return true;
}
return false;
}
};
jsFiddle
Based on Tushar's suggestion, (new Date().getTime()+'').substr(6,7) will give you a 7-digits unique number. Not random, but unique... at least for a certain time. Since it's based on timestamp, it is actually milliseconds time. Since the length is 7 digits, it will loop from 0000000 to 9999999 every 1000 seconds, that's roughly 17 minutes. But you can't expect a higher "level of uniqueness" with only 7 digits. It may be enough for your application, depending on what you want to achieve with that.
Edit
Following up with your comment : you want a unique SECOND number, so millisecond is no use to you (should have mentioned the seconds in your question).
You could give an invoice the number of the current second, which is (new Date().getTime()+'').substr(3,7). That would be perfect, if the unique number was not limited to 7 digits. This makes 9.999.999 possible seconds, which is... around 115 days.
With 9 digits, you could print unique invoices numbers every second for 316 years.
So in my opinion, your only solution is just to start counting invoices from 0000001, then 0000002, etc, and keeping track of that somewhere to ensure uniqueness. I don't get how generating a random number between 0 and 9.999.999 will make invoices numbering more efficient or easier to track.
For an invoice number you should guarantee a unique identifier no matter how long. In which case I'll suggest using an UUID/GUID. See http://guid.us/GUID/PHP for two implementations (windows-COM/non-windows). If you are not familiar with the concept of UUID, check wikipedia here: http://en.wikipedia.org/wiki/Universally_unique_identifier .
Another option for you is to use mysql's native uuid generator:
select uuid();
This you can also use in your insert queries, like:
insert into mytable (id, data) values (uuid(), 'mytext');
Of course your table structure needs to be adjusted. Personally I find it best to use traditional integer auto_inc id as primary key, along with a guid field to use in replication/export/etc as key.
EDIT:
There apparently is also a uuid_short() function in mysql (since 5.1.20).
SELECT UUID_SHORT();
92395783831158784
See https://dev.mysql.com/doc/refman/5.1/en/miscellaneous-functions.html#function_uuid-short
I am looking for a way to generate a unique ID for nosql database. Unlike relational database there is no idea of rows which means there is no last row to increment from.
The most common way to handle this is to use UUID's. But my problem is I need to add another ID (other than the UUID) which needs to be:
Unique
Unsigned Int32
Total data could reach around 50,000,000. So how would you generate somewhat unique uint32 ID's?
The UInt32 value type represents unsigned integers with values ranging from 0 to 4,294,967,295.
Only generated when a new user registers.
3 Id's are given to each new user.
Currently using Couchbase Server.
This problem has already been solved - I would suggest using the atomic Increment (or Decrement) functions in Couchbase - these are a common pattern to generate unique IDs.
Whenever the incr() method is called, it atomically increments the counter by the specified value, and returns the old value, therefore it's safe if two clients try to increment at the same time.
Pseudocode example (I'm no Node.JS expert!):
// Once, at the beginning of time we init the counter:
client.set("user::count", 0);
...
// Then, whenever a new user is needed:
nextID = client.incr("user::count", 1); // increments counter and returns 'old' value.
newKey = "user_" + nextID;
client.add(newKey, value);
See the Node.JS SDK for reference, and see Using Reference Doucments for Lookups section in the Couchbase Developer Guide for a complete usage example.
Here's a function that returns a unique identifier each time it's called. It should be fine as long as the number of items does not exceed the range of 32-bit integers, which seems to be the case given the described requirements. (warning: once the array of UIDs fills up, this enters an infinite loop. You may also want to create some sort of a reset function that can empty the array and thus reset the UID when necessary.)
var getUID = (function() {
var UIDs = [];
return function() {
var uid;
do {
uid = Math.random() * Math.pow(2, 32) | 0x0;
} while (UIDs[uid] !== undefined);
return UIDs[uid] = uid;
};
}());
if you will call this insert method by passing "user" as key then your docId will be auto increment as
user_0
user_1
user_2
etc...
Please note that couchbase will show one extra row in your bucket with key as meta id and next counter value as doc value. Do not get surprised if you use query like select count(*) total from table; as it will show one more than real count, to avoid use where clause so that this row won't be counted.
public insert(data: any, key: string) {
return new Promise((resolve, reject) => {
let bucket = CouchbaseConnectionManager.getBucket(`${process.env.COUCHBASE_BUCKET}`)
bucket.counter(key, 1, {initial:0}, (err:any, res:any)=>{
if(err){
this.responseHandler(err, res, reject, resolve);
}
const docId = key + "_" + res.value;
bucket.insert(docId, data, (error:any, result:any) => {
this.responseHandler(error, result, reject, resolve);
});
});
});
}