I have been working on a Rubik’s Cube Timer website, and I need to make a scrambling algorithm. I’ll go over how the scrambling algorithm should work:
Each face has it’s own letter, it’s initial. for examble, if you want to move the front face, you would write “ F “. If you want to move the the right face, you would write “ R “, and so on. just note that the bottom face is D, as for down. So you have D U R L B F.
If there is nothing after that letter, you turn it clockwise. If there is an appostrophe “ ‘ “, you turn it counter-clockwise. If there is a 2, you turn it two times. Now the thing is that you cannot have 2 same letters next to oneanother, as they would cancel (For example “.. U U’ ...” would be the same as doing nothing. So far, I have this taken care of in my algorithm.
The problem comes when you have one letter, then it’s opposite, then again the first letter, ( For example “.. U D U’...” (would mean Up clockwise, Down clockwise, Up counterclokwise)).
I have no idea how to check for these and avoid them automatically. Here’s the code:
<div id=“Scramble”></div>
<script>
generateScramble();
function generateScramble() {
// Possible Letters
var array = new Array(" U", " D", " R", " L", " F", " B")
// Possible switches
var switches = ["", "\'", "2"];
var array2 = new Array(); // The Scramble.
var last = ''; // Last used letter
var random = 0;
for (var i = 0; i < 20; i++) {
// the following loop runs until the last one
// letter is another of the new one
do {
random = Math.floor(Math.random() * array.length);
} while (last == array[random])
// assigns the new one as the last one
last = array[random];
// the scramble item is the letter
// with (or without) a switch
var scrambleItem = array[random] + switches[parseInt(Math.random()*switches.length)];
array2.push(scrambleItem); // Get letters in random order in the array.
}
var scramble = "Scramble: ";
// Appends all scramble items to scramble variable
for(i=0; i<20; i++) {
scramble += array2[i];
}
document.getElementById("Scramble").innerHTML = scramble; // Display the scramble
}
</script>
For starters God's Number is 20 for Rubik;s cube so you got only 20 moves instead of 25. I assume you are not doing scrambling (as your title suggest) but instead generate solution command strings for genere&test solver type. There are too many sequences that cancel each other and to check for all of them would be most likely slower than try them out actually.
The problem is that even O(n^20) is huge and you need to lower the 20. That is done by LUT holding semi solved states. For example create table holding states for all combinations of 5 turn scrambling. Then use that as end condition turning your solver into O(n^15 + n^5) = O(n^15) ...
Related
In a hybrid Android/Cordova game that I am creating I let users provide an identifier in the form of an Emoji + an alphanumeric - i.e. 0..9,A..Z,a..z - name. For example
🙋️Stackoverflow
Server-side the user identifiers are stored with the Emoji and Name parts separated with only the Name part requiried to be unique. From time-to-time the game displays a "league table" so the user can see how well they are performing compared to other players. For this purpose the server sends back a sequence of ten "high score" values consisting of Emoji, Name and Score.
This is then presented to the user in a table with three columns - one each for Emoji, Name and Score. And this is where I have hit a slight problem. Initially I had quite naively assumed that I could figure out the Emoji by simply looking at handle.codePointAt(0). When it dawned on me that an Emoji could in fact be a sequence of one or more 16 bit Unicode values I changed my code as follows
Part 1:Dissecting the user supplied "handle"
var i,username,
codepoints = [],
handle = "🙋️StackOverflow",
len = handle,length;
while ((i < len) && (255 < handle.codePointAt(i)))
{codepoints.push(handle.codePointAt(i));i += 2;}
username = handle.substring(codepoints.length + 1);
At this point I have the "disssected" handle with
codepoints = [128587, 8205, 65039];
username = 'Stackoverflow;
A note of explanation for the i += 2 and the use of handle.length above. This article suggests that
handle.codePointAt(n) will return the code point for the full surrogate pair if you hit the leading surrogate. In my case since the Emoji has to be first character the leading surrogates for the sequence of 16 bit Unicodes for the emoji are at 0,2,4....
From the same article I learnt that String.length in Javascript will return the number of 16 bit code units.
Part II - Re generating the Emojis for the "league table"
Suppose the league table data squirted back to the app by my servers has the entry {emoji: [128583, 8205, 65039],username:"Stackexchange",points:100} for the emoji character 🙇️. Now here is the bothersome thing. If I do
var origCP = [],
i = 0,
origEmoji = '🙇️',
origLen = origEmoji.length;
while ((i < origLen) && (255 < origEmoji.codePointAt(i))
{origCP.push(origEmoji.codePointAt(i);i += 2;}
I get
origLen = 5, origCP = [128583, 8205, 65039]
However, if I regenerate the emoji from the provided data
var reEmoji = String.fromCodePoint.apply(String,[128583, 8205, 65039]),
reEmojiLen = reEmoji.length;
I get
reEmoji = '🙇️'
reEmojiLen = 4;
So while reEmoji has the correct emoji its reported length has mysteriously shrunk down to 4 code units in place of the original 5.
If I then extract code points from the regenerated emoji
var reCP = [],
i = 0;
while ((i < reEmojiLen) && (255 < reEmoji.codePointAt(i))
{reCP.push(reEmoji.codePointAt(i);i += 2;}
which gives me
reCP = [128583, 8205];
Even curioser, origEmoji.codePointAt(3) gives the trailing surrogate pair value of 9794 while reEmoji.codePointAt(3) gives the value of the next full surrogate pair 65039.
I could at this point just say
Do I really care?
After all, I just want to show the league table emojis in a separate column so as long as I am getting the right emoji the niceties of what is happening under the hood do not matter. However, this might well be stocking up problems for the future.
Can anyone here shed any light on what is happening?
emojis are more complicated than just single chars, they come in "sequences", e.g. a zwj-sequence (combine multiple emojis into one image) or a presentation sequence (provide different variations of the same symbol) and some more, see tr51 for all the nasty details.
If you "dump" your string like this
str = "🙋️StackOverflow"
console.log(...[...str].map(x => x.codePointAt(0).toString(16)))
you'll see that it's actually an (incorrectly formed) zwj-sequence wrapped in a presentation sequence.
So, to slice emojis accurately, you need to iterate the string as an array of codepoints (not units!) and extract plane 1 CPs (>0xffff) + ZWJ's + variation selectors. Example:
function sliceEmoji(str) {
let res = ['', ''];
for (let c of str) {
let n = c.codePointAt(0);
let isEmoji = n > 0xfff || n === 0x200d || (0xfe00 <= n && n <= 0xfeff);
res[1 - isEmoji] += c;
}
return res;
}
function hex(str) {
return [...str].map(x => x.codePointAt(0).toString(16))
}
myStr = "🙋️StackOverflow"
console.log(sliceEmoji(myStr))
console.log(sliceEmoji(myStr).map(hex))
I have the following HTML:
<input type = "text" id = "pick"> <input type = "submit" value = "Submit" onclick = "guessWord()">
That runs my js function which works fine (with unrelated hiccups) on the first call. But if I change my text and submit it again without reloading my initial if/else statement behaves incorrectly. Specifically, the if/else is supposed to check if the user inputted word is in an array. It works properly on the first call, but after that it jumps to the else block even when it shouldn't.
Here is the js (apologies in advance for including the whole function, I'm just usually asked to include more code than I initially do):
function guessWord() {
var comWords, match, compWord = "";
var possWords = dictFive;
var firstFive = ["vibex", "fjord", "nymph", "waltz", "gucks"]; // note: right now choosing any of these words results in unexpected behavior -- either it doesn't accept them or it freezes.
var inputWord = document.getElementById("pick").value.toLowerCase().replace(/\s+/g, '');
if (possWords.includes(inputWord)) { // checks to see if the user inputted word is in our dictionary.i f not, requests a different word.
// start game loop:
// in order to try and get as much information as possible in the first few turns I start by guessing the five words in firstFive[]: vibex, fjord, nymph, waltz, gucks. together, these words give us information about 25 letters.
for (let d = 0; d < inputWord.length; d++) { // this loop will run for the length of the inputted word, making it scaleable so in the future the program could accept shorter or longer words. within the current scope it will always be 5.
compWord = firstFive[d]; // the computers word will loop through each word in firstFive[].
if (inputWord === compWord) { // if the word matches the user inputted word:
document.getElementById("otpt").innerHTML = "Your word was: " + firstFive[d] + ". I guessed it in " + (d + 1) + " turns.";
return;
} else { // if the word is not the user inputted word, then:
comWords = (inputWord + compWord).split('').sort().join(''); // we combine the users word with the comps word and sort them by character.
match = comWords.length - comWords.replace(/(\w)\1+/g, '$1').length; // match produces a numerical value for how many letters matched between both words.
for (let e = 0; e < possWords.length; e++) { // loop to cycle through our dictionary.
for (let f = 0; f < inputWord.length; f++) { // loop to cycle through all the different match options.
if (match === 0) { // if there are no matches we can:
if (possWords[e].includes(firstFive[f])) { // go through the dict and get rid of every word that has letters in common with the word.
possWords.splice(e, 1);
}
} else if (match === f) { // if there's at least one letter in common:
comWords = (possWords[e] + compWord).split('').sort().join(''); // as we cycle through the dict, pick each available word, combine and sort with the chosen word,
var matchFive = comWords.length - comWords.replace(/(\w)\1+/g, '$1').length; // and then find how many letters match.
if (matchFive != match) { // any words in dict that have a different match value can be deleted.
possWords.splice(e, 1);
}
}
}
}
}
}
// once we've worked through the words in firstFive[] we start guessing randomly.
for (let a = 0; a < possWords.length; a++) { // the loop max is set to the length of the array because that's the maximum amount of time the guessing can take.
compWord = possWords[Math.floor(Math.random() * possWords.length)]; // choose a random word.
if (compWord === inputWord) { // check if the random word is the inputted word. if it is:
document.getElementById("otpt").innerHTML = "Your word was: " + compWord + ". I guessed it in " + (a + 5) + " turns. I had " + possWords.length + " remaining words that were possible matches.";
return;
} else { // while the word still isn't correct:
comWords = (compWord + inputWord).split('').sort().join(''); // again, we join and sort it.
match = comWords.length - comWords.replace(/(\w)\1+/g, '$1'); // find its match value.
for (let c = 0; c < inputWord.length; c++) { // loop through inputted word's length to check all letters.
if (match === 0) { // again, no matches we can safely delete all words with those letters.
if (possWords.includes(compWord[c])) {
possWords.splice(c, 1);
}
} else if (match === c) { // if match is higher than 0:
for (let g = 0; g < possWords.length; g++) {
comWords = (possWords[g]+ compWord).split('').sort().join('');
matchAll = comWords.length - comWords.replace(/(\w)\1+/g, '$1');
if (match != matchAll) {
possWords.splice(g, 1);
}
}
}
}
}
}
} else { // If the user inputted word was not in our dictionary, requests a different word:
document.getElementById("otpt").innerHTML = "Please choose a different word.";
}
}
(For context, dictFive is an array located on a separate file.) The code is trying to guess the user inputted word by checking how many letters match and then splicing out words from the master array if they can't match, so the array possWords starts with about 2500 words and gets narrowed down to a few hundred by the end of the function. As far as I can tell, the function should be resetting the vars properly every time it's called, though, but I'm guessing it isn't for some reason?
Your dictFive array is being spliced each time the function is called.
When you set possWords = dictFive, and then splice possWords later, you're also splicing dictFive because both variables refer to the same array. Then, the second time the function is run, dictFive is still in its spliced state. Instead of setting possWords = dictFive, try making a copy of the array. That way, you'll splice the copy without affecting the original, dictFive. You can clone an array by possWords = dictFive.slice().
var dictFive = [0,1,2,3,4]; // Just an example of whatever dictFive might be
var possWords = dictFive; // This makes possWords refer to the same thing as dictFive
possWords.splice(0, 1); // Splicing the array at whatever point
possWords // [1,2,3,4] because the 0th element was spliced out
dictFive // also [1,2,3,4] because both dictFive and possWords are the same array
compare that to
var dictFive = [0,1,2,3,4];
var possWords = dictFive.slice(); // This makes a copy of the array instead of referencing the original dictFive
possWords.splice(0, 1);
possWords // [1,2,3,4];
dictFive // Now, this array is still [0,1,2,3,4] because only the possWords array was spliced. dictFive wasn't affected.
I'm working on a script to match scrambled words to their unscrambled counterpart, and I'm running into a weird NaN problem. I have inserted comments below to guide you further in understand my problem in hopes that you can give me an explanation as to why this problem is occurring. Thank you for your time and help.
NOTE: Two chunks of code that are exactly the same (but with different variable names) act differently. The latter works, but the former does not.
<!DOCTYPE html>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
</head>
<body>
<form id="words">
<input type="hidden" value="html:),121212,21122112,asdfjkl;,hal9000,1234qwer,1q2w3e,test123,gambit,sports,hello!,willie,hashtags,oneway,whoknows,whyowhy,youwho,theone,sweet!,wo0Oot">
</form>
<br>
<li>leho!l</li>
<li>lilwie</li>
<li>thahsags</li>
<li>yaneow</li>
<li>kwosonhw</li>
<li>ohhywwy</li>
<li>oyuwoh</li>
<li>otheen</li>
<li>e!stwe</li>
<li>wtoO0o</li>
<script>
var words, scram, wordVals, scramVals, sum, i, I;
// Turn word list into an array.
words = [];
var words = document.getElementById("words").elements[0].value.split(",");
// Turn scrambled word list into an array.
scram = [];
for (var i = 0; i < 10; i++) {
scram.push(document.getElementsByTagName("li")[i].innerHTML);
};
// Next I iterate through each letter of each word (for the unscrambled words)
// and convert those letters into ASCII values - adding those together
// to get the total ASCII value of each word.
wordVals = [];
for (i = 0; i < words.length; i++) {
for (I = 0; I < words[i].length; I++) {
sum += words[i].charCodeAt(I);
// console.log(sum)
// Using console.log(sum), you see that the first 6 iterations return NaN.
// Each iteration (ASCII Value) for the first word in this loop
// words[0].charCodeAt(0, 1, 2, 3, 4, 5, 6), all returned NaN.
// But each word after the first iteration calculates the ASCII values
// like it's supposed to.
};
wordVals.push(parseInt(sum));
sum = 0;
console.log("Word[" + i + "]: " + words[i] + " - Value: " + wordVals[i]);
};
// typeof WordVals[0] = number
console.log("typeof wordVals[0]?: " + typeof(wordVals[0]));
// isInteger = false
console.log("isInteger wordVals[0]?: " + Number.isInteger(wordVals[0]));
// Here I am iterating through each letter of each word (for the scrambled words)
// and converting those letters into ASCII values - adding those values together
// to get the total ASCII value of each word.
// **This loop uses the same exact code as the one above (but with different variables)
// and it works correctly. Why is this?
scramVals = [];
for (i = 0; i < scram.length; i++) {
for (I = 0; I < scram[i].length; I++) {
sum += scram[i].charCodeAt(I);
};
scramVals.push(sum);
sum = 0;
console.log("Scram Word[" + i + "]: " + scram[i] + " - Value: " + scramVals[i]);
};
</script>
</body>
</html>
OUTPUT:
Word[0]: html:) - Value: NaN
Word[1]: 121212 - Value: 297
...rest of list omitted...
typeof wordVals[0]?: number
isInteger wordVals[0]?: false
Scram Word[0]: leho!l - Value: 565
Scram Word[1]: lilwie - Value: 646
...rest of list omitted...
I have tried using a different starting word in my word list but get the same NaN result.
I don't understand why the first block of code doesn't work when it is practically identical to the second block of code that does work!
Why am I getting a NaN on the ASCII values of each letter of the first word?
I'm a beginner, and realize that this is not the proper way to go about finding word matches. Regular Expressions is probably what I will try to end up using, but I need to figure this out so I can progress in the way that I learn.
Thank you. I use this site all the time because I love the no nonsense approach most of you take in addressing user requests. There are a lot of really smart people here donating their time and intelligence, and I really appreciate it.
I am also open to any tips you can give me to improve this code.
That is because undefined + Number is NaN. Initially, the sum variable is undefined. that is why the first value in the wordVals array is NaN.
Just set sum to 0 and it should work as expected, ex:
var words, scram, wordVals, scramVals, sum = 0, i, I;
For the first iteration you adding a number with undefined( ie sum). So it'l become NaN. make sum = 0; at the beginning
I am new in programing and right now I am working on one program. Program need to find the substring in a string and return the index where the chain starts to be the same. I know that for that I can use "indexOf". Is not so easy. I want to find out substrings with at moste one different char.
I was thinking about regular expresion... but not really know how to use it because I need to use regular expresion for every element of the string. Here some code wich propably will clarify what I want to do:
var A= "abbab";
var B= "ba";
var tb=[];
console.log(A.indexOf(B));
for (var i=0;i<B.length; i++){
var D=B.replace(B[i],"[a-z]");
tb.push(A.indexOf(D));
}
console.log(tb);
I know that the substring B and string A are the lowercase letters. Will be nice to get any advice how to make it using regular expresions. Thx
Simple Input:
A B
1) abbab ba
2) hello world
3) banana nan
Expected Output:
1) 1 2
2) No Match!
3) 0 2
While probably theoretically possible, I think it would very complicated to try this kind of search while attempting to incorporate all possible search query options in one long complex regular expression. I think a better approach is to use JavaScript to dynamically create various simpler options and then search with each separately.
The following code sequentially replaces each character in the initial query string with a regular expression wild card (i.e. a period, '.') and then searches the target string with that. For example, if the initial query string is 'nan', it will search with '.an', 'n.n' and 'na.'. It will only add the position of the hit to the list of hits if that position has not already been hit on a previous search. i.e. It ensures that the list of hits contains only unique values, even if multiple query variations found a hit at the same location. (This could be implemented even better with ES6 sets, but I couldn't get the Stack Overflow code snippet tool to cooperate with me while trying to use a set, even with the Babel option checked.) Finally, it sorts the hits in ascending order.
Update: The search algorithm has been updated/corrected. Originally, some hits were missed because the exec search for any query variation would only iterate as per the JavaScript default, i.e. after finding a match, it would start the next search at the next character after the end of the previous match, e.g. it would find 'aa' in 'aaaa' at positions 0 and 2. Now it starts the next search at the next character after the start of the previous match, e.g. it now finds 'aa' in 'aaaa' at positions 0, 1 and 2.
const findAllowingOneMismatch = (target, query) => {
const numLetters = query.length;
const queryVariations = [];
for (let variationNum = 0; variationNum < numLetters; variationNum += 1) {
queryVariations.push(query.slice(0, variationNum) + "." + query.slice(variationNum + 1));
};
let hits = [];
queryVariations.forEach(queryVariation => {
const re = new RegExp(queryVariation, "g");
let myArray;
while ((searchResult = re.exec(target)) !== null) {
re.lastIndex = searchResult.index + 1;
const hit = searchResult.index;
// console.log('found a hit with ' + queryVariation + ' at position ' + hit);
if (hits.indexOf(hit) === -1) {
hits.push(searchResult.index);
}
}
});
hits = hits.sort((a,b)=>(a-b));
console.log('Found "' + query + '" in "' + target + '" at positions:', JSON.stringify(hits));
};
[
['abbab', 'ba'],
['hello', 'world'],
['banana', 'nan'],
['abcde abcxe abxxe xbcde', 'abcd'],
['--xx-xxx--x----x-x-xxx--x--x-x-xx-', '----']
].forEach(pair => {findAllowingOneMismatch(pair[0], pair[1])});
I'm a newbie, making a little exercise to practice with arrays. I've tried solving this from previous articles but none seem to have the relevant scenario.
I want to randomly generate sentences into paragraphs using phrases from an array. I got the random sentence generation part working fine.
var ipsumText = ["adventure", "endless youth", "dust", "iconic landmark", "spontaneous", "carefree", "selvedge","on the road", "open road", "stay true", "free spirit", "urban", "live on the edge", "the true wanderer", "vintage motorcyle", "american lifestyle", "epic landscape", "low slung denim", "naturaL"];
//a simple function to print a sentence //
var sentence = function (y) {
var printSentence = "";
for (i=0; i<7; i++) {
//random selection of string from array //
var x = Math.floor(Math.random() * 20);
printSentence += y [x] + " ";
}
return printSentence
};
console.log(sentence(ipsumText));
But now I want to be able to add a comma or full stop to the end of the sentence.
Because each word/phrase from the array used in the sentence prints with a space after it, I need to add an extra word with a full stop or comma right after it to avoid the space between them. To do this I created an extra variable
// create a word and full stop to end a sentence//
var addFullstop = ipsumText[Math.floor(Math.random() * ipsumText.length)] + ". ";
var addComma = ipsumText[Math.floor(Math.random() * ipsumText.length)] + ", ";
These variables work on their own how I expect. They print a random word with a comma or full stop right after them.
However now I can't work out how to get them to add to the end of the sentence. I have tried quite a few versions referencing articles here, but I'm missing something, because when I test it, I get nothing printing to the console log.
This is what I have most recently tried.
// add the sentence and new ending together //
var fullSentence = sentence(ipsumText) + addFullstop;
console.log(fullSentence)
Can someone explain why this wouldn't work? And suggest a solution to try?
thanks
See ES6 fiddle: http://www.es6fiddle.net/isadgquw/
Your example works. But consider a different approach which is a bit more flexible. You give it the array of words, how long you want the sentence to be, and if you want an ending to the sentence, pass in end, otherwise, just leave it out and it will not be used.
The first line generates an array of length count which is composed of random indices to be used to index into the words array. The next line maps these indices to actual words. The last line joins all of these into a sentence separated by a single space, with an optional end of the sentence which the caller specifies.
const randomSent = (words, count, end) =>
[...Array(count)].map(() => Math.floor(Math.random() * words.length))
.map(i => words[i])
.join(' ') + (end || '')
randomSent (['one','two','x','compound word','etc'], 10, '! ')
// x x one x compound word one x etc two two!
To make it more flexible, consider making a function for each task. The code is reusable, specific, and no mutable variables are used, making it easy to test, understand, and compose however you like:
const randInt = (lower, upper) =>
Math.floor(Math.random() * (upper-lower)) + lower
const randWord = (words) => words[randInt(0, words.length)]
const randSentence = (words, len, end) =>
[...Array(len)].map(() => randWord(words))
.join(' ') + (end || '')
const randWordWithEnd = (end) => randWord(ipsumText) + end
const randWordWithFullStop = randWordWithEnd('. ')
const randWordWithComma = randWordWithEnd(', ')
// Now, test it!
const fullSentWithEnd = randSentence(ipsumText, 8, '!!')
const fullSentNoEnd = randSentence(ipsumText, 5)
const fullSentComposed = fullSentNoEnd + randWordWithFullStop
Link again for convenience: http://www.es6fiddle.net/isadgquw/