Reverse order of players on boardgame.io - javascript

in boerdgame.io framework ( for building a turn-based game ) how do I reverse the turns order as the response of some move?
I implemented it by myself using G to store the order.
And give a custom function to handle that
Taki = {
turn:{
order: {
first: ({G, ctx}) => 0,
next: ({G, ctx} ) => {
const next = 1 + G.skip
return (ctx.numPlayers + ctx.playOrderPos + G.direction * next) % ctx.numPlayers
},
},
}
}

Related

API call to youtube.videos.list failed with error

When I run the following JavaScript through Google Apps script with more then 100 keywords.
function youTubeSearchResults() {
// 1. Retrieve values from column "A".
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const values = sheet.getRange("A2:A" + sheet.getLastRow()).getDisplayValues().filter(([a]) => a);
// 2. Retrieve your current values.
const modifyResults = values.flatMap(([keywords]) => {
const searchResults = YouTube.Search.list("id, snippet", { q: keywords, maxResults: 10, type: "video", order: "viewCount", videoDuration: "short", order: "date" });
const fSearchResults = searchResults.items.filter(function (sr) { return sr.id.kind === "youtube#video" });
return fSearchResults.map(function (sr) { return [keywords, sr.id.videoId, `https://www.youtube.com/watch?v=${sr.id.videoId}`, sr.snippet.title, sr.snippet.publishedAt, sr.snippet.channelTitle, sr.snippet.channelId, `https://www.youtube.com/channel/${sr.snippet.channelId}`, sr.snippet.thumbnails.high.url] });
});
// 3. Retrieve viewCounts and subscriberCounts.
const { videoIds, channelIds } = modifyResults.reduce((o, r) => {
o.videoIds.push(r[1]);
o.channelIds.push(r[6]);
return o;
}, { videoIds: [], channelIds: [] });
const limit = 50;
const { viewCounts, subscriberCounts } = [...Array(Math.ceil(videoIds.length / limit))].reduce((obj, _) => {
const vIds = videoIds.splice(0, limit);
const cIds = channelIds.splice(0, limit);
const res1 = YouTube.Videos.list(["statistics"], { id: vIds, maxResults: limit }).items.map(({ statistics: { viewCount } }) => viewCount);
const obj2 = YouTube.Channels.list(["statistics"], { id: cIds, maxResults: limit }).items.reduce((o, { id, statistics: { subscriberCount } }) => (o[id] = subscriberCount, o), {});
const res2 = cIds.map(e => obj2[e] || null);
obj.viewCounts = [...obj.viewCounts, ...res1];
obj.subscriberCounts = [...obj.subscriberCounts, ...res2];
return obj;
}, { viewCounts: [], subscriberCounts: [] });
const ar = [viewCounts, subscriberCounts];
const rr = ar[0].map((_, c) => ar.map(r => r[c]));
// 4. Merge data.
const res = modifyResults.map((r, i) => [...r, ...rr[i]]);
// 5. Put values on Spreadsheet.
sheet.getRange(2, 2, res.length, res[0].length).setValues(res);
}
it gives me that error
GoogleJsonResponseException: API call to youtube.videos.list failed with error:
The request cannot be completed because you have exceeded your quota.
reduce.viewCounts #code.gs:23
youTubeSearchResults #code.gs:20
I know YouTube have data call limits for example you can call the results of not more then 50 video ids at one time but if you have 1000 video ids in your sheet you can run then loop for first 50 then next so on. Is it anything like that I can do with search results too.
Please help me understand how can I fix this issue.
Note that the endpoint the most expensive in your script is the Search: list one which costs 100 of your 10,000 quota (you can have a look to other endpoint costs here).
You may be interested in the standalone quota-free solution that consists in reverse-engineering the YouTube UI search feature.
Otherwise a temporary solution to Google audit consists in using my no-key service.
With my no-key service:
const searchResults = YouTube.Search.list("id, snippet", { q: keywords, maxResults: 10, type: "video", order: "viewCount", videoDuration: "short", order: "date" });
would become:
const searchResults = JSON.parse(UrlFetchApp.fetch(`https://yt.lemnoslife.com/noKey/search?part=snippet&q=${keywords}&maxResults=10&type=video&order=viewCount&videoDuration=short`).getContentText())
As part=id doesn't add more data to the response and AFAIK using two order isn't supported by YouTube Data API v3.

How to implement Page-based pagination

Im trying to implement Page-based pagination on this particular endpoint but the problem Im faced is that the response body is not printing out the page, page_size and total results.
The URL is http://localhost:3006/events/tickets?sort_by=name&page=1&page_size=2
How do I get to return a Json body like this ?
Ideal Response Im trying to achieve
{
content: [], // respponse items
page: 1, // current page
results_per_page: 10, // results per page
total_pages: 10, // total number of pages
total_results: 100 // number of all results
}
const getTickets = (tickets) => {
return new Promise(async (resolve, reject) => {
try {
let query = `SELECT id, name, description, ticket_price, ticket_type
FROM tickets`;
let sort_by = tickets.sort_by;
if(sort_by ?? false) {
query += ` ORDER BY ${sort_by}`;
if (sort_by) {
query += (sort_by == 'desc' ? ` DESC` : ` ASC`);
}
}
let limit = tickets.page_size;
let replacements = { limit: limit };
if (limit) {
query += ` LIMIT :limit`;
replacements.limit = limit;
}
let offset = limit ? ((tickets.page - 1) * limit) : 0;
if(offset) {
query += ` OFFSET :offset `;
replacements.offset = offset;
};
const ticket = await db.sequelize.query(
query,
{
replacements: replacements,
type: QueryTypes.SELECT,
}
);
return resolve(ticket);
} catch (err) {
return reject(err)
}
})
}
[
{
"id": 1,
"name": "Tennis Finals",
"description": "Finals: Tennis Team 1 vs Tennis Team 2",
"ticket_price": 25,
"ticket_type": 1
},
{
"id": 5,
"name": "Testing Tickets",
"description": "Description....",
"ticket_price": 1000,
"ticket_type": 30
}
]

Taking the Average of a Data Set in Firebase Database

I'm in the process of designing an app but coming a bit unstuck with Javascript. So far I have a Firebase Realtime Database with the following structure.[!
What I'd like to do is for each time an area in green is added / updated, take a value(red) from that area in green get the average from all the values that are held within each green object and place it into a brown object at the bottom.
Would anyone have any idea on how to complete this using Javascript / Firebase functions?
JSON Export:
{
"5Rz8DpU34PeXAcnriD6vEiPu7jk2" : {
"UiWK7RkdeCbUte8g7naB9qp42qu1" : {
"rating1" : 5
},
"average" : 0
},
"Fi43uP2LcbVLi2uFwUyCAp2uvSH2" : {
"average" : 0
},
"UiWK7RkdeCbUte8g7naB9qp42qu1" : {
"Fi43uP2LcbVLi2uFwUyCAp2uvSH2" : {
"rating1" : 5,
"rating2" : 5
},
"asdas" : {
"rating1" : 2
},
"average" : 0
},
"gov4hRpDgDVhyVgsQrYJnn1rfeW2" : {
"UiWK7RkdeCbUte8g7naB9qp42qu1" : {
"rating1" : 5
},
"average" : 0
}
}
The following Cloud Function code should do the trick:
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.database();
exports.average = functions.database
.ref('/ratings/{blueId}/{greenId}')
.onWrite((change, context) => {
const blueId = context.params.blueId;
const blueRef = db.ref('ratings/' + blueId);
const blueAverageRef = db.ref('ratings/' + blueId + '/average');
let totalSum = 0;
let nbrOfElem = 0;
return blueRef
.once('value', function(snapshot) {
snapshot.forEach(function(childSnapshot) {
if (childSnapshot.val().val) {
//console.log(childSnapshot.val());
totalSum += childSnapshot.val().val;
nbrOfElem++;
}
});
})
.then(() => {
//console.log('totalSum: ' + totalSum);
//console.log('nbrOfElem: ' + nbrOfElem);
return blueAverageRef.transaction(function(average) {
if (nbrOfElem > 0) {
return { val: totalSum / nbrOfElem };
} else {
return 0;
}
});
})
.catch(error => {
console.log(error);
});
});
Note that it uses a Transaction, see https://firebase.google.com/docs/database/web/read-and-write#save_data_as_transactions and https://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction
The database shall be structured as:
-ratings
-blueNode1
-greenNode11
-val:2 // <- red node in your picture
-greenNode12
-val:10
-average // <- red node in your picture
-val:6
-blueNode2
-greenNode21
-val:5
-greenNode22
-val:3
-greenNode23
-val:1
-average
-val:5
It would be something like this:
exports.detectGreenChanges = functions.database.ref('/requests/{blueId}/{greenId}').onWrite((change, context) => {
const greenIdData = change.after.val();
const blueId = context.params.blueId;
const greenId = context.params.greenId;
// Do whatever you want with the data and set it to where ever you want
});
Take a look at this docs, it'll help you set the value back to where you want.
Good luck!

In Rx.js, how can I distinguish which stream triggers the combineLatest method?

I'm writing my own version of who to follow?. Clicking refreshButton will fetching suggestions list and refresh <Suggestion-List />, and closeButton will resue the data from suggestions list and refresh <Suggestion-List-Item />.
I want to let the closeClick$ and suggestions$ combine together to driving subscribers.
Demo code here:
var refreshClick$ = Rx.Observable
.fromEvent(document.querySelector('.refresh'), 'click')
var closeClick$ = Rx.Observable.merge(
Rx.Observable.fromEvent(document.querySelector('.close1'), 'click').mapTo(1),
Rx.Observable.fromEvent(document.querySelector('.close2'), 'click').mapTo(2),
Rx.Observable.fromEvent(document.querySelector('.close3'), 'click').mapTo(3)
)
var suggestions$ = refreshClick$
.debounceTime(250)
.map(() => `https://api.github.com/users?since=${Math.floor(Math.random()*500)}`)
.startWith('https://api.github.com/users')
.switchMap(requestUrl => Rx.Observable.fromPromise($.getJSON(requestUrl)))
Rx.Observable.combineLatest(closeClick$, suggestions$, (closeTarget, suggestions) => {
if (/* the latest stream is closeClick$ */) {
return [{
target: clickTarget,
suggestion: suggestions[Math.floor(Math.random() * suggestions.length)]
}]
}
if (/* the latest stream is suggestions$ */) {
return [1, 2, 3].map(clickTarget => ({
target: clickTarget,
suggestion: suggestions[Math.floor(Math.random() * suggestions.length)]
}))
}
})
Rx.Observable.merge(renderDataCollectionFromSuggestions$, renderDataCollectionFromCloseClick$)
.subscribe(renderDataCollection => {
renderDataCollection.forEach(renderData => {
var suggestionEl = document.querySelector('.suggestion' + renderData.target)
if (renderData.suggestion === null) {
suggestionEl.style.visibility = 'hidden'
} else {
suggestionEl.style.visibility = 'visible'
var usernameEl = suggestionEl.querySelector('.username')
usernameEl.href = renderData.suggestion.html_url
usernameEl.textContent = renderData.suggestion.login
var imgEl = suggestionEl.querySelector('img')
imgEl.src = "";
imgEl.src = renderData.suggestion.avatar_url
}
})
})
You can find it in JsFiddle.
You should note the comments in condition judgment, closeClick$ emits [{ target: x, suggestion: randomSuggestionX }], suggestions$ emits [{ target: 1, suggestion: randomSuggestion1 }, { target: 2, suggestion: randomSuggestion2 }, { target: 3, suggestion: randomSuggestion3 }]. Subsriber render interface according to the emitted data.
May there are some ways/hacks to distinguish the latest stream in combineLatest or elegant modifications?
I think the easiest way would be to use the scan() operator and always keep the previous state in an array:
Observable.combineLatest(obs1$, obs2$, obs3$)
.scan((acc, results) => {
if (acc.length === 2) {
acc.shift();
}
acc.push(results);
return acc;
}, [])
.do(states => {
// states[0] - previous state
// states[1] - current state
// here you can compare the two states to see what has triggered the change
})
Instead of do() you can use whatever operator you want of course.
Or maybe instead of the scan() operator you could use just bufferCount(2, 1) that should emit the same two arrays... (I didn't test it)

Generate a RxJS Observable from a sequence of Promises returned from an ORM

Am trying to generate an Observable from a sequence of (batched) records from a SQL database, I am trying to run through all the records in the database. Am using an ORM on node-js, Sequelize which returns the records wrapped in a promise.
I have defined a function fetchbatch() which fetches the next batch and returns a Promise[Array[Record]] and flatMap'ing the result to an Observable.
My condition(to terminate) is set as a global in the then block of the promise based on whether the query returned no records, but the callback is never called, only promises are returned infinitely and so the termination condition is never satisfied. Any suggestions on how this is handled? Here is a gist of the code.
function getAllPaginated(conditions) {
var remaining = true;
var batch_size = 20;
function condition(){ return remaining; }
function selector(promisedBatchOfRecords){
//console.log(promisedBatchOfRecords);
//return Observable.fromPromise(promisedBatchOfRecords[1]);
return (promisedBatchOfRecords[1]);
}
function fetchBatch(batchNumberAndBatch) { // Returns [NextBatchNumber, Promise[Array[Record]]]
//console.log(remaining);
var batch_number = batchNumberAndBatch[0];
var offset = (batch_number - 1) * batch_size;
var rs = Records.findAll({where: conditions, offset: offset, limit: batch_size});
return [batch_number + 1,
rs.then(function(batch) {
console.log(batch.length);
if (!(batch.length > 0)){
remaining = false;
};
return batch.map(function(r){r.dataValues});
})];
}
return Observable.generate(fetchBatch([1, []]), condition, fetchBatch).flatMap(Ramda.identity/*over the promise*/).flatMap(Ramda.identity/*over the list*/);
}
var o = getAllPaginated({where: {a: "b"}})
o.subScribeOnNext(console.log)
You can try something like that :
const result = new Rx.Subject;
const batch_size = 3;
// Init the recursion
whileFind(0)
.subscribe();
// Grab the result here
result
.mergeAll()
.map(batch => batch.dataValues)
.subscribe(value => console.log(value));
// Recursion function
function whileFind(offset) {
return Rx.Observable.fromPromise(findAll(offset))
.concatMap(batch => {
if (batch.length <= 0) { // Stop condition
return Rx.Observable.of(null);
}
else {
result.next(batch); // Push the chunk to the result
return whileFind(offset + batch_size);
}
});
}
// Emulate Records.findAll from your BO
function findAll(offset): Promise<Object[]> {
const data = [
{ dataValues: 1 },
{ dataValues: 2 },
{ dataValues: 3 },
{ dataValues: 4 },
{ dataValues: 5 },
{ dataValues: 6 },
{ dataValues: 7 },
{ dataValues: 8 },
{ dataValues: 9 },
{ dataValues: 10 }
];
return Promise.resolve(data.slice(offset, offset + batch_size));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.0-beta.12/Rx.min.js"></script>

Categories

Resources