I followed PedroTech's guide for a Wordle Clone to practice React, which I really enjoy. I've mostly had no issues and have made some additions myself but am having a hell of a time getting my gameOver condition to trigger on a win (guessing the correct word). It triggers correctly on a loss (6 guesses without the right word). The issue would seem at first blush to be a casing mismatch between the correct word from the word bank and the current word being concatted in the for loop, but upon logging each I am getting identical strings. I have tried everything in my toolbelt at this point and am at a loss.
Here is where the correct word and states are set. The correct word is set from a lowercase bank using use effect and is logged upon comp mount. I know it is correct because I am getting my letter colors to fill correctly upon guess. I will post an image to give a better idea of what I'm looking at.
`function App() {
const [board, setBoard] = useState(emptyBoard);
const [wordSet, setWordSet] = useState(new Set());
const [currentGuess, setCurrentGuess] = useState({ attempt: 0, letterPosition: 0 });
const [gamesWon, setGamesWon] = useState(0);
const [soupInfo, setSoupInfo] = useState([]);
const [soupPic, setSoupPic] = useState(null);
const [userPersist, setUserPersist] = useState(false);
const [validWord, setValidWord] = useState('');
const [disabledLetters, setDisabledLetters] = useState([]);
const [gameOver, setGameOver] = useState({ gameOver: false, guessedWord: false })
const [correctWord, setCorrectWord] = useState('');
const [playerPosition, setPlayerPosition] = useState(1);
// let [currentWord, setCurrentWord] = useState('');
let [soupIndex, setSoupIndex] = useState([]);
useEffect(() => {
genWordSet().then((words) => {
setWordSet(words.wordSet);
setCorrectWord(words.todaysWord);
});
}, []);`
Here is an image of the correct word being recognized letter by letter:
soup-seeker
Here is my onEnter() function that recognizes keydown input of a 5-letter word. This is where the gameOver state should change upon correctWord and currentWord being matched:
const onEnter = () => {
if (currentGuess.letterPosition !== 5) return;
let currentWord = '';
for (let i = 0; i < 5; i++) {
// setCurrentWord(board[currentGuess.attempt][i].toString())
currentWord += board[currentGuess.attempt][i].toLowerCase();
}
console.log(currentWord);
console.log(correctWord);
console.log(typeof currentWord);
console.log(typeof correctWord);
// + `\r`
//not sure why this \r is appearing in the word set but can just concatenate
if (wordSet.has(currentWord.toLowerCase() + `\r`)) {
setCurrentGuess({ attempt: currentGuess.attempt + 1, letterPosition: 0 });
console.log(currentWord)
console.log(correctWord);
} else {
setValidWord('Not null');
} if (currentWord === correctWord) {
setGameOver({ gameOver: true, guessedWord: true });
return;
};
if (currentGuess.attempt === 5) {
setGameOver({ gameOver: true, guessedWord: false })
return;
};
//increase the array index w attempt; reset position in array to start for next guess
};
I have a tried lower casing the current word in the if condition but it doesn't seem to do anything, and they are both registering as a lowercase strings anyways.
Could anyone help me out? I'm pretty new to this so my guess is I'm missing something really obvious. Usually I try to solve things myself but this one has me really stumped.
Thank you.
Related
I have the following code
const readline = require('readline');
const {stdin, stderr} = process;
const rl = readline.createInterface({
output: stderr,
input: stdin
})
console.log(rl.getCursorPos());
let i = 5;
while (i > 0) {
console.log('fill up with logs');
i--
}
console.log(rl.getCursorPos())
and its output
{ cols: 2, rows: 0 }
fill up with logs
fill up with logs
fill up with logs
fill up with logs
{ cols: 2, rows: 0 }
as from above the last 'getCursorPos()' should return a new position since the stderr output has been filled up with logs, instead, it returns its default value, did I misunderstand the way it works?
or is there anyway I can get cursor position and save it multiple times using readline? I've looked through using asni escape, but it seems like it can only store one cursor position at a time.
The position returned by readline is relative to the current cursor location, not absolute on the screen.
I was able to create a promise to get this information using the answer below containing the escape sequence for requesting terminal cursor position:
How can I get position of cursor in terminal?
const getCursorPos = () => new Promise((resolve) => {
const termcodes = { cursorGetPosition: '\u001b[6n' };
process.stdin.setEncoding('utf8');
process.stdin.setRawMode(true);
const readfx = function () {
const buf = process.stdin.read();
const str = JSON.stringify(buf); // "\u001b[9;1R"
const regex = /\[(.*)/g;
const xy = regex.exec(str)[0].replace(/\[|R"/g, '').split(';');
const pos = { rows: xy[0], cols: xy[1] };
process.stdin.setRawMode(false);
resolve(pos);
}
process.stdin.once('readable', readfx);
process.stdout.write(termcodes.cursorGetPosition);
})
const AppMain = async function () {
process.stdout.write('HELLO');
const pos = await getCursorPos();
process.stdout.write("\n");
console.log({ pos });
}
AppMain();
I can determine the terminal window size with process.stdout.columns, process.stdout.rows, and can recalculate my positioning with the resize event.
I am struggling to find out how to get my current cursor position for doing something like updating a specific location on my terminal, then returning to the previous location.
Does Node offer something like process.stdeout.x, process.stdeout.y to tell me where I currently am?
I realise I there are some Linux specific work arounds, but is there something offered by Node that allows for this functionality in a cross platform way?
The position returned by readline is relative to the current cursor location, not absolute on the screen.
I was able to create a promise to get this information using the answer below containing the escape sequence for requesting terminal cursor position:
How can I get position of cursor in terminal?
const getCursorPos = () => new Promise((resolve) => {
const termcodes = { cursorGetPosition: '\u001b[6n' };
process.stdin.setEncoding('utf8');
process.stdin.setRawMode(true);
const readfx = function () {
const buf = process.stdin.read();
const str = JSON.stringify(buf); // "\u001b[9;1R"
const regex = /\[(.*)/g;
const xy = regex.exec(str)[0].replace(/\[|R"/g, '').split(';');
const pos = { rows: xy[0], cols: xy[1] };
process.stdin.setRawMode(false);
resolve(pos);
}
process.stdin.once('readable', readfx);
process.stdout.write(termcodes.cursorGetPosition);
})
const AppMain = async function () {
process.stdout.write('HELLO');
const pos = await getCursorPos();
process.stdout.write("\n");
console.log({ pos });
}
AppMain();
if (!args[1]) return message.channel.send('You need to specify a person!')
if (!args[2]) return message.channel.send('Please specify a time for how long the ban council will last.')
var tim = args[2]
const sweden = message.mentions.users.first()
message.react('👍').then(() => message.react('👎'))
const mappo = ['👍', '👎']
if (!args[1]) return message.channel.send('Please specify a person!')
if(message.guild){ //this will check if the command is being called from a server
const embad = new Discord.MessageEmbed()
.setTitle('Ban Council')
.addField('The Convicted:', `${sweden.tag}`)
.addField('Rules:', 'Vote on whether the convicted is guilty or not with the prompted reactions. The ban will end automatically in 5 seconds.')
message.channel.send(embad)
setTimeout(function(){
if(sweden){
const lyft = message.guild.member(sweden)
if(lyft){
if(message.reactions.cache.map(r => `${'👍'} ${r.users.cache.size}`)[0] > message.reactions.cache.map(r => `${'👍'} ${r.users.cache.size}`)[1]){
lyft.ban({ ression: 'Majority has exiled you from server. '}).then(() => {
message.reply(`The user ${december.tag} was banned as a result of a majority vote.`)
})
} else {
message.channel.send('The ban was cancelled.')
}
}else{
message.reply('The user is not in this server.')
}
}else{
message.reply('You need to specify a person!')
}
}, tim)
} else {
message.channel.send('Banning does not work here!')
}
It sends the "Ban cancelled" before it actually has the chance to take input. I've tried collectors, and it doesn't work because of the max: part, how can I resolve this problem? (Also it returns no errors)
This is in a case. (appending onto what feedback I got)
Firstly you should beautify your code before you post on stackoverflow, you could have removed the break keyword and just explain it was inside of a switch case
The reason it does not work is because you are checking the message's reaction, and not the embed that you send, so to fix this you need to assign a variable to message.channel.send(embad), but this is a promise so you need to await it, which requires an async function
lastly awaitReactions and createReactionCollector are probably better options,
So here's the new code:
(async () => {
if (!args[1]) return message.channel.send('You need to specify a person!');
if (!args[2]) return message.channel.send('Please specify a time for how long the ban council will last.')
if (!message.guild) return message.channel.send('Banning does not work here');
var tim = args[2]
const sweden = message.mentions.member.first()
const mappo = ['👍', '👎']
message.react('👍').then(() => message.react('👎'))
const embad = new Discord.MessageEmbed()
.setTitle('Ban Council')
.addField('The Convicted:', `${sweden.tag}`)
.addField('Rules:', 'Vote on whether the convicted is guilty or not with the prompted reactions. The ban will end automatically in 5 seconds.')
const embedMessage = await message.channel.send(embad);
setTimeout(function () {
if (!sweden) return message.reply('You need to mention a person');
const filter = (reaction) => mappo.includes(reaction.emoji.name);
embad.awaitReactions(filter, { time: 5000 })
.then(collection => {
//not the most optimal way to do it
const emoji1 = collection.find(e => e.emoji.name === '👍');
const emoji2 = collection.find(e => e.emoji.name === '👎');
//both default to 0
const upvotes = emoji1 && emoji1.users.size || 0;
const downvotes = emoji2 && emoji2.users.size || 0;
if (upvotes > downvotes) {
lyft.ban({ reason: 'Majority has exiled you from server. ' })
.then(() => message.reply(`The user ${december.tag} was banned as a result of a majority vote.`));
} else {
message.channel.send('The ban was cancelled.');
}
})
.catch(console.error);
}, tim);
})();
It's been around 8 months since I made this post, and I found the answer, and an even more effective way to do it. I won't post the entire code, as it's pretty long and not very neat. However, this is a much more effective way of counting reactions.
My Original Code was:
const upvotes = message.reactions.cache.map(r => ${'👍'} ${r.users.cache.size})[0]
const downvotes = message.reactions.cache.map(r => ${'👎'} ${r.users.cache.size})[1]
It doesn't work very well either.
const [upvoteReaction, downvoteReaction] = message.reactions.cache.first(2);
const upvotes = upvoteReaction.users.cache.size;
const downvotes = downvoteReaction.users.cache.size;
It takes the number of reactions from the first two reactions on the message. Seeing as how the only reactions are thumbs up and thumbs down, it will get the numbers from both of them. From there, you can just compare the two.
I have this code which just reads in data from a .csv file and converts it to json and logs the data:
const fs = require('fs');
const path = require('path');
const sd = path.resolve(__dirname + '/fixtures/SampleData.csv');
const strm = fs.createReadStream(sd).setEncoding('utf8');
const Rx = require('rxjs/Rx');
const csv2json = require('csv2json');
const dest = strm
.pipe(csv2json({
separator: ','
}));
dest.on('error', function(e){
console.error(e.stack || e);
})
const obs = Rx.Observable.fromEvent(dest, 'data')
.flatMap(d => Rx.Observable.timer(100).mapTo(d))
obs.subscribe(v => {
console.log(String(v));
})
What the code is doing is logging all the data after a 100 ms delay. I actually want to delay on each line of data and log each line after a small delay.
The above code doesn't achieve that - what is the best way to control the rate at which the data is logged?
Hypothesis: All the lines of data come in approximately at the same time, so all are delayed 100 ms, so they end up getting printed at pretty much the same time. I need to only start delaying the next line after the previous as been logged.
the following code seems to do the same thing as using the timer above:
const obs = Rx.Observable.fromEvent(dest, 'data')
.delay(100)
Hypothesis: All the lines of data come in approximately at the same
time, so all are delayed 100 ms, so they end up getting printed at
pretty much the same time. I need to only start delaying the next line
after the previous as been logged.
Your hypothesis is correct
Solution
Swap out the .flatMap() in your original solution with .concatMap()
Rx.Observable.from([1,2,3,4])
.mergeMap(i => Rx.Observable.timer(500).mapTo(i))
.subscribe(val => console.log('mergeMap value: ' + val));
Rx.Observable.from([1,2,3,4])
.concatMap(i => Rx.Observable.timer(500).mapTo(i))
.subscribe(val => console.log('concatMap value: ' + val));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.3/Rx.js"></script>
This will ensure that every emission completes before the next emission is subscribed to and starts delaying its value.
I couldn't find the functionality I needed in the RxJS library (although it might be there, I just couldn't find it, let me know if there is a better, more idiomatic, way).
So I wrote this, which seems to do the job:
const fs = require('fs');
const path = require('path');
const sd = path.resolve(__dirname + '/fixtures/SampleData.csv');
const strm = fs.createReadStream(sd).setEncoding('utf8');
const Rx = require('rxjs/Rx');
const csv2json = require('csv2json');
const p = Rx.Observable.prototype;
p.eachWait = function(timeout){
const source = this;
const values = [];
let flipped = true;
const onNext = function (sub){
flipped = false;
setTimeout(() => {
var c = values.pop();
if(c) sub.next(c);
if(values.length > 0){
onNext(sub);
}
else{
flipped = true;
}
}, timeout);
}
return Rx.Observable.create(sub => {
return source.subscribe(
function next(v){
values.unshift(v);
if(flipped){
onNext(sub);
}
},
sub.error.bind(sub),
sub.complete.bind(sub)
);
});
}
const dest = strm
.pipe(csv2json({
separator: ','
}));
dest.on('error', function(e){
console.error(e.stack || e);
});
const obs = Rx.Observable.fromEvent(dest, 'data')
.eachWait(1000)
obs.subscribe(v => {
console.log(String(v));
});
I assume this is as about as performant as you can make it - only one timer should be running at any given moment.
I have Redis with a lot of keys in some format and I want to get keys that match some pattern and do some operations on them. I don't use KEYS method since it's not recommend in production. Using SCAN I'm wondering what is the best way to write it in code. I have to do something like a while loop but using promises, my current solution looks like this (code is simplified a little):
'use strict'
const Promise = require('bluebird');
const config = require('./config');
const client = require('./clinet');
let iterator = 0;
Promise.coroutine(function* () {
do {
iterator = yield clinet.scanAsync(iterator, 'myQuery', 'COUNT', config.scanChunkSize)
.then(data => {
let nextIterator = data[0];
let values = data[1];
//do some magic with values
return nextIterator;
})
} while (iterator !== '0');
})();
Is there a better way to do it that I'm missing?
I realize this is a really old question, but I found all of the other answers very unsatisfying. Here is yet another attempt to scan in a relatively clean way using async await (WITHOUT the use of yet another external dependency). You can easily modify this to continuously delete each set of found keys (you would want to tackle them in batches like this in case there are LOTS). Pushing them into an array just demonstrates one very basic thing you could do with them during this stage.
const redis = require('redis');
const { promisify } = require('util');
const client = redis.createClient({...opts});
const scan = promisify(client.scan).bind(client);
const scanAll = async (pattern) => {
const found = [];
let cursor = '0';
do {
const reply = await scan(cursor, 'MATCH', pattern);
cursor = reply[0];
found.push(...reply[1]);
} while (cursor !== '0');
return found;
}
You can use recursion to keep calling scan until done.
function scanAsync(cursor, pattern, returnSet){
return redisClient.scanAsync(cursor, "MATCH", pattern, "COUNT", "100").then(
function (reply) {
cursor = reply[0];
var keys = reply[1];
keys.forEach(function(key,i){
returnSet.add(key);
});
if( cursor === '0' ){
return Array.from(returnSet);
}else{
return scanAsync(cursor, pattern, returnSet)
}
});
}
Pass in a Set() to make sure keys aren't duplicated
myResults = new Set();
scanAsync('0', "NOC-*[^listen]*", myResults).map(
function( myResults ){ console.log( myResults); }
);
You can try this snippet to scan (1000) keys per iteration and 'delete`.
var cursor = '0';
function scan(pattern,callback){
redisClient.scan(cursor, 'MATCH',pattern,'COUNT', '1000', function(err, reply){
if(err){
throw err;
}
cursor = reply[0];
if(cursor === '0'){
return callback();
}else{
var keys = reply[1];
keys.forEach(function(key,i){
redisClient.del(key, function(deleteErr, deleteSuccess){
console.log(key);
});
});
return scan(pattern,callback);
}
});
}
scan(strkey,function(){
console.log('Scan Complete');
});
Nice option for node-redis module is to use scan iterators. Example:
const redis = require("redis");
const client = redis.createClient();
async function getKeys(pattern="*", count=10) {
const results = [];
const iteratorParams = {
MATCH: pattern,
COUNT: count
}
for await (const key of client.scanIterator(iteratorParams)) {
results.push(key);
}
return results;
}
(Of course you can also process your keys on the fly in for await loop without storing them in additional array if that's enough for you).
If you do not want to override scan parameters (MATCH/COUNT) you can just skip them and execute client.scanIterator() without parameter (defaults will be used then, MATCH="*", COUNT=10).
I think the node bindings for Redis are pushing too much responsibility to the caller here. So I created my own library for scanning as well, using generators in node:
const redis = require('redis')
const client = redis.createClient(…)
const generators = require('redis-async-gen')
const { keysMatching } = generators.using(client)
…
for await (const key of keysMatching('test*')) {
console.info(key)
}
It's the last bit that obviously is the thing that you should care about. Instead of having to carefully control an iterator yourself, all you need to do is use a for comprehension.
I wrote more about it here.
Go through this, it may help.
https://github.com/fritzy/node-redisscan
do not use the library as it, go through the code available at
https://github.com/fritzy/node-redisscan/blob/master/index.js