Queue logic and a while - javascript

Hi I have a row of players,
and from this row of players I get a player that matches two conditions
const condition = (5 / 100) * playerOne.mmr + playerOne.mmr
const condition2 = playerOne.mmr - ((5 / 100) * playerOne.mmr);
find another player who is between 5% less and 5% more than my player's mmr value
and then I did it
searching(playerOne) {
const condition = (5 / 100) * playerOne.mmr + playerOne.mmr
const condition2 = playerOne.mmr - ((5 / 100) * playerOne.mmr);
const player = playerOne;
const playerTwo = this.players.find((playerTwo) => playerTwo.mmr < condition && playerTwo.mmr > condition2 && playerTwo.id != playerOne.id);
while(!this.players.find((playerTwo) => playerTwo.mmr < condition && playerTwo.mmr > condition2 && playerTwo.id != playerOne.id)){
const playerTwo = this.players.find((playerTwo) => playerTwo.mmr < condition && playerTwo.mmr > condition2 && playerTwo.id != playerOne.id);
console.log(this.players);
}
const matchedPlayers = [
player,
playerTwo
]
// remove matched players from this.players
this.removePlayers(matchedPlayers);
// return new Match with matched players
return matchedPlayers;
}
}
Good if I can't find a compatible mmr I go into the while forever
And even on my server I adding a player to the queue after
inside my while my queue continues with the same players and the new player I added in the queue does not appear
queue.addPlayer(new Player(1,'spt',970));
queue.addPlayer(new Player(2,'test2',1000));
queue.addPlayer(new Player(3,'test3',1050));
queue.addPlayer(new Player(4,'test4',70));
const playerOne = queue.players.find((playerOne) => playerOne.mmr === 70);
const players = queue.searching(playerOne);
queue.addPlayer(new Player(5,'test6',75));
I added my 5 player after I called my search function
and inside my whilei I put to give console.log in my queue, but only 4 players appear and not my 5 player so it is in the infinite loop and I don't know where I went wrong or how I can fix or if my logic is very failed
[
Player { id: 1, name: 'spt', mmr: 970 },
Player { id: 2, name: 'test2', mmr: 1000 },
Player { id: 3, name: 'test3', mmr: 1050 },
Player { id: 4, name: 'test4', mmr: 70 }
]

You need to make the searching happen in an asynchronous manner. There are a bunch of ways to accomplish it. Personally I am a fan of promises. Below is showing a basic way of creating a way to wait until a player is added in the range. I also added the ability to cancel the search.
const players = [
{ id: 1, name: 'spt', mmr: 970 },
{ id: 2, name: 'test2', mmr: 1000 },
{ id: 3, name: 'test3', mmr: 1050 },
{ id: 4, name: 'test4', mmr: 70 }
]
const makeMatch = (id) => {
let timer
let promiseResolve
let promiseReject
const promise = new Promise((resolve, reject) => {
promiseResolve = resolve
promiseReject = reject
});
const playerDetails = players.find(p => p.id == id)
const { mmr } = playerDetails
const findMatchup = () => {
const secondPlayer = players.find(p =>
p.id !== id &&
Math.abs(p.mmr - mmr) <= 10)
if (secondPlayer) {
console.log("match!", id)
promiseResolve({ player1: playerDetails, player2: secondPlayer })
} else {
console.log("no matches", id)
timer = window.setTimeout(findMatchup, 1000)
}
}
findMatchup()
return {
kill: () => {
timer && window.clearTimeout(timer)
promiseReject('cancelled')
},
promise
}
}
// Make up a match with player 4
// There are no matches so it will loop for awhile
var matchMaking = makeMatch(4)
matchMaking.promise.then( ({ player1, player2 }) => {
console.log(player1, player2)
})
// Act like a new player signed on, push in the player
// We should be a match after this happens
window.setTimeout(() => {
console.log("Player is added");
players.push({ id: 5, name: 'test5', mmr: 75 })
}, 5500)
//Example showing killing off
// Make up a match with player 1
// There are no matches so it will loop for awhile
var matchMaking2 = makeMatch(1)
matchMaking2.promise.then( ({ player1, player2 }) => {
console.log(player1, player2)
}).catch((details) => {
console.log(details);
})
// Acting like user clicked cancel button to stop playing
window.setTimeout(() => {
console.log("Player has cancelled");
matchMaking2.kill()
}, 10000)

Related

Add member counter to bot activity discord.js v.14

I tried to do that but it isn't working. How can I resolve this?
I want that the activiy is the current member Count (refreshing every 5 minutes(or when its possible everytime a member joined))
module.exports = {
name: 'ready',
once: true,
async execute(client) {
setInterval(() => {
let membersCount = client.guilds.cache.map(guild => guild.memberCount).reduce((a, b) => a + b, 0)
}, 1000 * 60 * 5);
const options = [
{
type: ActivityType.Playing,
text: `mit [${membersCount} usern]`,
status: "online"
}
];
await client.user.setPrecence({
activities: [{
name: [options].text,
type: [options].type
},
],
status: [options].status
}).catch(console.error);
}
}
The problem with the code is just the positioning of the update function. What is happening right now is that, every five minutes, the setInterval() runs and updates the memberCount variable. But, the await client.user.setPresence() only runs one time when the async execute() function was first called . So to fix your code, you would just have to move the options variable and await client.user.setPresence() to inside the setInterval() function. Your fixed code would look like this:
module.exports = {
name: 'ready',
once: true,
async execute(client) {
setInterval(() => {
let membersCount = client.guilds.cache.map(guild => guild.memberCount).reduce((a, b) => a + b, 0)
const options = [
{
type: ActivityType.Playing,
text: `mit [${membersCount} usern]`,
status: "online"
}
];
await client.user.setPrecence({
activities: [{
name: options[0].text,
type: options[0].type
}],
status: options[0].status
}).catch(console.error);
}, 1000 * 60 * 5);
}
}

Reducing Array of Objects to a single output

Hi I am trying to figure out the best way to achieve something. I essentially receive a lot of data like so
[
{
name: 'email.delivered',
programme: 'Email One',
timestamp: 2022-03-24T18:06:02.000Z,
"payload":{
"id":"bcabca7c-a5d5-4e02-b247-2292240ffc77",
},
},
{
name: 'interaction.click',
programme: 'Email One',
timestamp: 2022-03-24T18:06:02.000Z,
"payload":{
"correlation":{
"channel":"email",
"messageId":"bcabca7c-a5d5-4e02-b247-2292240ffc77",
},
"metadata":{
"customerId":"9999999111",
"programName":"Email One"
}
},
},
...
]
The name is the event, can be delivered, failed, expired or click. The programme is what I want things categorised by. So I am doing some checks beforehand, and my code (removed a bit for simplicity) at the moment is like so
emails.forEach((record) => {
const data = JSON.parse(record.data);
if (data?.name === 'email.delivered') {
const id = data?.payload?.id;
if (!deliverDates[id]) deliverDates[id] = record.timestamp;
deliveredMap.set(programme, (deliveredMap.get(programme) || 0) + 1);
return;
}
if (data?.name === 'email.click') {
const id = data?.payload?.correlation?.messageId;
if (id) {
const deliveryDate = new Date(deliverDates[id]);
if (deliveryDate.getTime() > Date.now() - 1209600000) {
const programme = record?.programme;
clicksMap.set(programme, (clicksMap.get(programme) || 0) + 1);
}
}
}
});
The problem with the above is now I now have two Maps, when really I want just one Object returned with the programme as the key. For each programme I want to count all the different event types. So what I would like to see is something more like this
{
'Email One': {
delivered: 315,
failed: 18,
expired: 14,
click: 27,
},
'Email Two': {
delivered: 542,
failed: 322,
expired: 33,
click: 22,
}
...
}
How can I achieve this?
Thanks
Use a helper function to record the event occurence:
const eventCountsByProgramme = {};
function recordOccurence(programme, event) {
const eventCounts = (eventCountsByProgramme[programme] ??= {});
eventCounts[event] = (eventCounts[event] ?? 0) + 1;
}
Then use
recordOccurence(programme, 'delivered');
instead of
deliveredMap.set(programme, (deliveredMap.get(programme) || 0) + 1);
and
recordOccurence(programme, 'click');
instead of
clicksMap.set(programme, (clicksMap.get(programme) || 0) + 1);

Infinitegrid duplicates API calls

I use vue-infinitegrid and I have realized in a browser that a backend API is called three times. Some code first (git):
<GridLayout
ref="ig"
:options="options"
:layoutOptions="layoutOptions"
#append="onAppend"
#layout-complete="onLayoutComplete"
#image-error="onImageError"
>
<div slot="loading">Loading...</div>
<div class="item" v-for="(item) in list" :key="item.key">
<ViewItem :item="item"/>
</div>
</GridLayout>
data() {
return {
start: 0,
loading: false,
list: [],
isEnded: false,
options: {
isOverflowScroll: false,
useFit: true,
useRecycle: true,
horizontal: false,
align: 'center',
transitionDuration: 0.2,
},
layoutOptions: {
margin: 15,
align: 'center',
},
pageSize: 3,
};
},
methods: {
async onAppend({ groupKey, startLoading }) {
this.$log.debug(`onAppend group key = ${groupKey}`);
const { list } = this;
if (this.isEnded) return;
const items = await this.loadItems();
startLoading();
this.list = list.concat(items);
},
async loadItems() {
const start = this.start || 0, size = parseFloat(this.pageSize), { tag } = this;
this.$log.debug(`loadItems start = ${start}, size = ${size}`);
let res = await this.$store.dispatch('GET_ITEM_STREAM', { start, size, tag });
if (res.length === 0) { // todo or smaller than requested
this.$log.debug('loadItems no data');
this.isEnded = true;
this.$refs.ig.endLoading();
return res;
}
if (this.exceptItem) {
res = res.filter(item => item._id !== this.exceptItem._id);
}
this.start = start + res.length;
this.$log.debug('loadItems finished');
return res;
},
onLayoutComplete({ isLayout, endLoading }) {
this.$log.debug(`onLayoutComplete isLayout = ${isLayout}`);
if (!isLayout) {
endLoading();
}
},
And some logs:
onAppend group key =
ItemList.vue:71 loadItems start = 0, size = 3
items.js:132 GET_ITEM_STREAM {"start":0,"size":3}
See more tips at https://vuejs.org/guide/deployment.html
ItemList.vue:83 loadItems finished
ItemList.vue:87 onLayoutComplete isLayout = false
ItemList.vue:62 onAppend group key =
ItemList.vue:71 loadItems start = 3, size = 3
items.js:132 GET_ITEM_STREAM {"start":3,"size":3}
ItemList.vue:62 onAppend group key =
ItemList.vue:71 loadItems start = 3, size = 3
items.js:132 GET_ITEM_STREAM {"start":3,"size":3}
2 ItemList.vue:83 loadItems finished
ItemList.vue:87 onLayoutComplete isLayout = false
ItemList.vue:62 onAppend group key =
ItemList.vue:71 loadItems start = 6, size = 3
items.js:132 GET_ITEM_STREAM {"start":6,"size":3}
ItemList.vue:62 onAppend group key =
ItemList.vue:71 loadItems start = 6, size = 3
items.js:132 GET_ITEM_STREAM {"start":6,"size":3}
2 ItemList.vue:83 loadItems finished
ItemList.vue:87 onLayoutComplete isLayout = false
I can see that start is incremented after onAppend is called. It looks like some concurrency issue, that the infinitegrid component does not wait until the REST call is finished and fires new event. Has anybody any experience with this component and knows how to handle this situation when I need to wait for a backend response?
Update
I have replaced async call with fixed data and it started to work correctly. So the trouble is with async.
// let items = await this.$store.dispatch('GET_ITEM_STREAM', { start, size, tag });
let items = [{ ...
Update:
Code sandbox with minimum reproducible scenerio: https://w56p2.csb.app/
The symptoms are different now, probably exhibiting the root cause - the event is emitted before the previous is processed.
https://github.com/naver/egjs-infinitegrid/issues/365
https://naver.github.io/egjs-infinitegrid/storybook/?path=/story/loading-bar-with-data-delay--grid-layout
In startLoading and endLoading, the loading bar appears and disappears, and some functions are temporarily disabled (moveTo, useFit).
The append and prepend work and must be prevented through the isProcessing method.
onAppend({ groupKey, startLoading, currentTarget }) {
if (currentTarget.isProcessing()) {
return;
}
}

Replace images one after in different period of times

I want to replace foundImg with loadingImg, for families array. The first one after 2sec then the second after 3....
This code replaces them at the same time
const loadingImg = require('./assets/25.gif');
const foundImg = require('./assets/done_Icon.png');
class App extends Component {
state = {
families: [
{id:0, name: "Accident History", duration: 2000, image: loadingImg},
{id:1, name: "Title Information", duration: 3000, image: loadingImg},
{id:2, name: "Liens", duration: 5000, image: loadingImg}
],
}
componentDidMount(){
const newFamilies = this.state.families;
newFamilies.map(item => {
item.image = foundImg;
setTimeout(() => this.setState({families: newFamilies}),item.duration)
})
}
Try this snippet
componentDidMount() {
this.state.families.forEach(item => {
setTimeout(() => {
const families = this.state.families.slice();
const itemFound = families.filter(i => i.id === item.id)[0];
itemFound.image = foundImg;
this.setState({families: families})
}, item.duration)
})
}
Avoid using map when you don't transform the array. Use foreach instead.

Vue Firestore same item keep repeating on scroll

I'm using VueJS 2 and Firestore for this project.
I have an infinite loading method, where I need the next item to load, when the user hits the bottom of the page on scroll.
Everything is in the same method called getStraps()
The idea is to have a first set of items to load, and when the user hits the bottom, then it should load the next batch of items.
Main problem: The first two items are loading as usual, and then another one is loading. But then when I scroll another time, the 4th item repeats to be the same as the 3rd as so on. The variable "lastVisible" doesn't seem to update, so it won't load the following items with "startAfter"
Video: http://recordit.co/TwqEb4SeWe
getStraps() {
var strapper = db.collection("straps");
var first = strapper.limit(2);
return first.get().then(documentSnapshots => {
var lastVisible =
documentSnapshots.docs[documentSnapshots.docs.length - 1];
console.log("first last visible", lastVisible);
const straps = [];
documentSnapshots.forEach(doc => {
const data = {
id: doc.id,
title: doc.data().title,
price: doc.data().price,
skin: doc.data().skin,
type: doc.data().type,
imgs: doc.data().imgs[0].url,
colors: doc.data().colors,
desc: doc.data().desc,
date: doc
.data()
.date.toString()
.slice(0, 15)
};
straps.push(data);
});
this.straps = straps;
var next = strapper.startAfter(lastVisible).limit(1);
window.onscroll = () => {
let bottomOfWindow =
document.documentElement.scrollTop + window.innerHeight ===
document.documentElement.offsetHeight;
if (bottomOfWindow) {
this.fetchingData = true;
console.log("fetch", this.fetchingData);
return next.get().then(documentSnapshots => {
var lastVisible =
documentSnapshots.docs[documentSnapshots.docs.length - 1];
console.log("last last", lastVisible);
if (documentSnapshots.empty) {
this.fetchingData = false;
this.noMoreStraps = true;
} else {
documentSnapshots.forEach(doc => {
const straps = this.straps;
const data = {
id: doc.id,
title: doc.data().title,
price: doc.data().price,
skin: doc.data().skin,
type: doc.data().type,
imgs: doc.data().imgs[0].url,
colors: doc.data().colors,
desc: doc.data().desc,
date: doc
.data()
.date.toString()
.slice(0, 15)
};
console.log("more data", data);
straps.push(data);
this.fetchingData = false;
});
this.straps = straps;
}
});
}
};
});
},

Categories