Friday, March 31, 2023

Coding Challenge #38 Show Reddit comments without pressing button

This script would be convenient in a plugin to show all comments for a post

 

 // for plugin: display whole reddit post & responses without site JS enabled
d = document.getElementsByTagName('div');
console.log("total DIVs: " + d.length);
var firstPost = null;
for(var xint = 0, len = d.length;xint < len;xint++){
if(d[xint].getAttribute('data-scroller-first') != null){
console.log('found one');
console.log(d[xint]);
firstPost = d[xint];
break;
}
}

firstPost.style.maxHeight = "100%";
firstPost.parentElement.style.maxHeight = "100%";
firstPost.parentElement.parentElement.style.maxHeight = "100%"; // show all posts
// but white fade will still be over bottom-most post

// remove ads under topic post
postRoot = firstPost.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement;
postRoot.removeChild(postRoot.children[2]);

//work thus far on expanding reddit no extra scripts
/*
top most part of post
.parentElement.children[2]
that is who we want to delete (ads, irrelevant misc stuff)
[0] is tabindex
[1] is post
[2] is ads below with videos that autoplay when scrolled to (ew!)
*/

Thursday, March 30, 2023

Coding Challenge #37 Find element with href in parents

// this traces through a given element's parents, trying to find any elements with an href
// wanted to find all inherited ways the click would send the user to another page

function traceAncestry(ele){
    var levelsAbove = 0;
    do{
        levelsAbove++;
        ele = ele.parentElement;
        if(ele.href != undefined && ele.href != ""){
            console.log("====== LV " + levelsAbove + ", vvv link to: " + ele.href);
            console.log(ele);
        }
    }while(ele != document.body && ele != null && levelsAbove < 1000);
    
    if(levelsAbove == 1000) console.log("Quit due to timeout. Probably infinite loop.");
    console.log("Traced through " + (levelsAbove-1) + " levels, checking for HREF attribute.");
}

Wednesday, March 29, 2023

Coding Challenge #36 Useless text compression

 // i was wondering if something like this would act as compression for text.
// answer: not really. to be more space efficient that the original text, it
// would require that the original text be very redundant with long words.
// example of beneficial string: "asdf asdf asdf asdf asdf"
// in most other cases, the output length would be equal to or greater than
// the length of the original string.

// change variable 'string' to compress the value it stores.

var dotChar = '.'.charCodeAt(0);
var qmarkChar = '?'.charCodeAt(0);
var expChar = '!'.charCodeAt(0);

var words = []; // unique words
var text = []; // the byte array output
function getwi(str){ // get word index
    for(var xint = 0, len = words.length;xint < len;xint++){
        if(words[xint] == str) return xint;
    }
    if(words.length == 31) words.push(''); // save index 32
    else if(words.length == dotChar || words.length == qmarkChar || words.length == expChar) words.push('');
    words.push(str);
    return words.length-1;
}

var string = `hi there
friend, what is up there is up there?`;

var tmp = ""; // tmp str buffer
function doit(){
    for(var xint = 0, len = string.length;xint < len;xint++){
        var c = string.charAt(xint);
        if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')){
            // is part of alphanumeric string that makes up word
            tmp += c;
        }else{
            // first, use previous tmp str buffer as a word
            if(tmp != ""){
                text.push( getwi(tmp) ); // add to dictionary & text output
                tmp = "";
            }
            
            // then decide what to do with this new char
            if(c == ' ') text.push(32);
            else if(c == '.' || c == '?' || c == '!') text.push(c.charCodeAt(0)); // common symbols are space-optimized
            else text.push( getwi(c) ); // other symbols are not
        }
    }
    
    // report findings
    console.log("\n=========");
    console.log("Found " + words.length + " unique words/punctuation");
    var size = 0;
    // size of all words in the dictionary
    for(var xint = 0, len = words.length;xint < len;xint++)
        size += words[xint].length;
    size += words.length; // +1 byte per words for the word length
    console.log("Dictionary size: " + size);
    console.log("Text data size: " + text.length);
    size += text.length;
    console.log("Total size: " + size);
    console.log("vs Original text size: " + string.length);
}

Tuesday, March 28, 2023

Coding Challenge #35

 // from Coursera's course "C for Everyone: Programming Fundamentals", week 5 peer graded assignment
// goal was to read a file to get the weights of elephant seals, then find the average of the weights.
// the file is space-tab sperated (ikr, what?). so a line would look like...
// 1234     4321     6123     1563
// and it would also be line-separated (as displayed in my browser) so... yeah.
#include<stdio.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// so the seal file when i read it was (space+tab) separated... ok *shrug*
// turn the raw file string into a vector of int's
std::vector<int> separateValues(std::string rawString){
    std::vector<int> v;
    std::string tmp = "";
    for(int xint = 0, len = rawString.length();xint < len;xint++){
        if(rawString[xint] == ' ' && (xint+1) < len && rawString[xint+1] == '\t'){ // space followed by tab; end of current value
            try{
                v.push_back(stoi(tmp)); // turn string value into number
            }catch(...){
                cout << "Exception: it was probably that the file ends in a space and therefore the resulting string is not a number." << endl;
            }
            tmp = ""; // empty string buffer
            xint++; // don't forget to skip tab by forwarding xint
        }else tmp += rawString[xint];
    }
    if(tmp != ""){
        try{
            v.push_back(stoi(tmp)); // turn string value into number
        }catch(...){
            cout << "Exception: it was probably that the file ends in a space and therefore the resulting string is not a number." << endl;
        }
    }
    return v;
}
int main(void)
{
    std::string wholeFile = "";
    /*
    // so, awkward... but i don't actually have the file to read.
    // i'll just pretend like i'm reading it.
    // to enable this to compile (onlinegdb.com,etc), i've commented-out this section
    #include <fstream>
    ifstream ifs;
    ifs.open("myData.tsv");
    if (!ifs.is_open()){ // check it was opened; to prevent infinite hdd-reading loop without yield. Oh, C++, you rascal ;)
        cout << "Error; file could not be opened." << endl;
        ifs.close();
        return 1;
    }
    std::string buffer;
    while(!ifs.eof()){ getline(ifs,buffer); wholeFile += buffer; }
    ifs.close();
    */
    std::vector<int> v = separateValues(wholeFile);
    int avg = 0;
    int total = 0;
    for(int xint = 0, len = v.size();xint < len;xint++)
        total += v[xint];
    
    if(v.size() > 0) avg = total / v.size(); // payload: get the average. don't divide by 0 :/
    
    cout << "Your average, sir: " << avg << endl;


return 0;
}

Monday, March 27, 2023

Coding Challenge #34

 # Edabit challenge "Convert Age to Days"
# in Ruby
# trying a new language...
def ageToDays(years)
    return years * 365
end

def test(n)
    puts "An age of " + n.to_s + " has passed " + ageToDays(n).to_s + " days."
end

test(1)
test(3)
test(5)
test(10)
test(89)
test(130)

Sunday, March 26, 2023

Coding Challenge #33

 // based off of Daily Coding Challenge #33
// print the running average
// Example: [2,1,3] prints...
// 2 (2/1=2)
// 1.5 (2+1=3, 3/2=1.5)
// 2 (2+1+3=6, 6/3=2)
function doit(ary){
    for(var xint = 0;xint < ary.length;xint++){
        var total = ary[0];
        for(var yint = 1;yint < (xint+1);yint++){
            total += ary[yint];
        }
        console.log(total / (xint+1));
    }
}
doit([2,1,3]);

Saturday, March 25, 2023

Coding Challenge #32

 // Daily Coding Challenge #34
// make a (made up) pallindrome from a word using as few characters as possible
// Example: race -> ecarace
// Example: google -> elgoogle
// if the word is already a pallindrome, it will be nigh doubled (eke -> ekeke)
function pallindromize(str){
    // we'll always be wrapping around the first letter
    // get the first letter, keep going until we get another of that letter
    // if the first & second instance of the letter have mirrored chars between them, just add the reversed proceeding letters to the front
    // else we use a substring of 2nd char onwards reversed as a prepending string for the original string
    var fromwhere = 0; // after this index is reversed and prepended
    for(var xint = 1;xint < str.length;xint++){
        if(str.charAt(xint) == str.charAt(0)){
            // check chars between are mirrored
            var lvar = 0; // first instance of the char, left one
            var rvar = xint; // 2nd instance of the char, right one
            var matched = true;
            while(lvar < rvar){
                if(str.charAt(lvar) != str.charAt(rvar)){
                    matched = false;
                    break;
                }
                lvar++;
                rvar--;
            }
            if(matched) fromwhere = xint;
            break;
        }
    }
    
    // reverse end of pallindrome, prepend to mid-char-n-later of pallindrome
    var s = "";
    for(var xint = str.length - 1;xint > fromwhere;xint--){
        s += str.charAt(xint);
    }
    return s + str;
}

function test(str){
    console.log(str + " -> " + pallindromize(str));
}

test('horse'); // -> esrohorse
test('poopy'); // -> ypoopy
test('om'); // -> mom

Friday, March 24, 2023

Coding Challenge #31

 // based off of Daily Coding Challenge #36
// sort an array of random characters 'R', 'G', and 'B' into the order R, G, B
// Example: [G,B,R,R,B,R,G] -> [R,R,R,G,G,B,B]
function array_extractStringIndices(ary, str){
    var o = [];
    for(var xint = 0;xint < ary.length;xint++)
        if(ary[xint] == str)
            o.push(ary[xint]);
    return o;
}
function array_appendIndices(ary, indices){
    for(var xint = 0;xint < indices.length;xint++)
        ary.push(indices[xint]);
    return ary;
}
function sort(a){
    var Rs = array_extractStringIndices(a, 'R');
    var Gs = array_extractStringIndices(a, 'G');
    var Bs = array_extractStringIndices(a, 'B');
    var o = [];
    o = array_appendIndices(o, Rs);
    o = array_appendIndices(o, Gs);
    o = array_appendIndices(o, Bs);
    return o;
}
if(sort(['G','B','R','R','B','R','G']).toString() == ['R','R','R','G','G','B','B'].toString())
    console.log('algorithm PASS');
else
    console.log('algorithm FAIL');

Thursday, March 23, 2023

Coding Challenge #30

// Are we in the upward slope or downward slope of the business cycle?
// Using m as a chart, get the trend of m over time(indices) to determine whether overall there is currently economic expansion or contraction.
var m = []; // monthly economic output
// m's values will be between 1 and 10 (inclusive)
for(var xint = 0;xint < 10;xint++) m.push(Math.floor(Math.random() * 9) + 1);

// get the overall trend
// the change is the accumulative difference between each index and the first index
var increases = 0;
var decreases = 0;
var change = m[0];
for(var xint = 1;xint < 10;xint++){
    if(m[xint] > m[xint-1]) increases++;
    else if(m[xint] < m[xint-1])decreases++;
    change += m[xint] - m[0];
}

console.log("m: " + m.toString());
console.log("Months of increased output: " + increases + " and " + decreases + " decreases.");
console.log("The overall change was " + change);
if(increases > decreases) console.log("More months where an increase occurred.");
else if(increases < decreases) console.log("More months where a decrease occurred.");
else console.log("Equal months of increases/decreases.");
if(change > m[0]) console.log("Overall, economic output is on the rise; expansion.");
else if(change < m[0]) console.log("Overall, economic output is on the decline; contraction.");
else console.log("Overall, economic output is consistent; economic output relatively unchanged.");

Wednesday, March 22, 2023

Coding Challenge #29 Salary Calculator

 // a simple module to provide quick calculations to figure out hourly/monthly/yearly income rates when given one of them.
function salaryCalculator(){
    this.cash = 0;
    // 40 hour week * 52 weeks per year = 2080
    this.setHourly = function(c){
        this.cash = this.get2Decis(c * 2080);
    }
    this.setYearly = function(c){
        this.cash = c;
    }
    this.setMonthly = function(c){
        this.cash = this.get2Decis(c * 12);
    }
    this.getHourly = function(){
        return this.get2Decis(this.cash / 2080);
    }
    this.getYearly = function(){
        return this.cash;
    }
    this.getMonthly = function(){
        return this.get2Decis(this.cash / 12);
    }
    this.get2Decis = function(n){
        return Math.floor(n * 100) / 100;
    }
}

var n = new salaryCalculator();
n.setHourly(12);
console.log("Current minimum wage of " + n.getHourly() + "/hr is " + n.getMonthly() + " per month, and " + n.getYearly() + " per year.");

Tuesday, March 21, 2023

Coding Challenge #28

 // from https://www.codecademy.com/resources/blog/20-code-challenges/
// reverse the order of words in a sentence, while maintaining punctuation
// we will maintain the punctuation '.' ',', '?', and '!'.
function reverse(str){
    var words = str.split(" ");
    // extract punctuation and keep it in an array
    var puncs = []; // store info of what punctuation is at which char in the word
    for(var xint = 0;xint < words.length;xint++){
        var punc = '';
        for(var yint = 0, len = words[xint].length;yint < len;yint++){
            var c = words[xint].charAt(yint);
            if(c == '.' || c == ',' || c == '?' || c == '!'){
                punc = c;
            }
        }
        puncs.push(punc);
        words[xint] = words[xint].replaceAll('.','').replaceAll(',','').replaceAll('?','').replaceAll('!',''); // remove punctuation
    }
    
    // rearrange words (reverse order)
    var words_rev = [];
    for(var xint = words.length - 1;xint > -1;xint--)
        words_rev.push(words[xint]);
        
    
    // place back in the punctuation
    for(var xint = 0;xint < words_rev.length;xint++){
        words_rev[xint] += puncs[xint];
    }
    
    
    var o = "";
    if(words_rev.length > 0){
        o += words_rev[0];
    }
    for(var xint = 1;xint < words_rev.length;xint++){
        o += " " + words_rev[xint];
    }
    return o;
}

function test(str){
    console.log("Pre : " + str + "\nPost: " + reverse(str));
}

test("Hi, there, friend.");
test("How are you today?");
test("I think, and therefore I am. How about you? Cool!");

Monday, March 20, 2023

Coding Challenge #27

// Daily Coding Challenge #31
// calculate the edit distance between two strings
// the edit distance is calculated by how many characters additions, deletions, and substitutions take place in order to get to the new word.
// Example: 'kitten' -> 'sitting' = 3 (k->s, e->i, add g)
// Example: 'round' -> 'pound' = 1 (r->p)
function calcEditDistance(str1, str2){
    // we're using str1 as the starting point, and calculating changes to get to string 2
    var edist = Math.abs(str1.length - str2.length);
    for(var xint = 0, len = Math.min(str1.length,str2.length); xint < len;xint++){
        if(str1.charAt(xint) != str2.charAt(xint)) edist++;
    }
    return edist;
}


function test(str1, str2, expected_output){
    var s = "Testing '" + str1 + "' against '" + str2 + "'. Was it the expected " + expected_output + "? ";
    if(calcEditDistance(str1, str2) == expected_output){
        s += "YES, PASS";
    }else{
        s += "NO, FAIL";
    }
    console.log(s);
}

// test (str1, str2, expected output)
test('hi there', 'hi theory', 3); // should PASS
test('pear', 'bear', 1); // should PASS
test('pear', 'boar', 1); // should FAIL
test('doodle', 'doodle', 0); // should PASS

Sunday, March 19, 2023

Coding Challenge #26

 // from https://www.codecademy.com/resources/blog/20-code-challenges/
// reverse the order of words in a sentence without paying attention to punctuation.
// as there was a higher level counterpart to this, this version was supposed to be extremely quick and bare-bones
function reverse(str){
    var words = str.split(" ");
    var o = "";
    for(var xint = words.length - 1;xint > -1;xint--){
        o += words[xint] + " ";
    }
    return o;
}

Saturday, March 18, 2023

Coding Challenge #25

 // from https://www.codecademy.com/resources/blog/20-code-challenges/
// test if a given number is prime
// we will test this by checking if it is greater than 1 (if not, fail)
// we will then multiply numbers below it to try to achieve the given number (if we cannot achieve it, it is prime: success)
function isPrime(n){
    if(n < 2) return false; // too small to be prime
    // since only 2+ can multiply together to be prime, we only need to check [2, half of n]
    for(var xint = 2, len = Math.ceil(n / 2) + 1;xint < len;xint++){
        for(var yint = 2;yint < len;yint++){
            var x = xint * yint;
            if(x == n) return false; // achieved the given number as a product
            if(x > n) break; // no further numbers would pass either
        }
    }
    return true;
}

Friday, March 17, 2023

Coding Challenge #24

// from https://www.codecademy.com/resources/blog/20-code-challenges/
// Printing numbers 1 through 100 (inclusive), but for multiples of 3, we will print "Fizz" instead. If it's a multiple of 5: "Buzz". If it's a multiple of both 3 and 5: "FizzBuzz".
// We will detect the multiples of 3 and/or 5 by seeing if the quotient of the current number divided by three changes when floored (to determine whether it went into it fully, producing an integer output).
function count(){
    var o = "";
    for(var xint = 1;xint < 101;xint++){
        if(xint / 3 == Math.floor(xint / 3)
        && xint / 5 == Math.floor(xint / 5)) o += "FizzBuzz\n"; // both 3 & 5
        else if(xint / 3 == Math.floor(xint / 3)) o += "Fizz\n"; // 3
        else if(xint / 5 == Math.floor(xint / 5)) o += "Buzz\n"; // 5
        else o += xint + "\n";
    }
    console.log(o);
}

Thursday, March 16, 2023

Coding Challenge #23

 // Daily Coding Challenge #28
// justify text with extra spaces between words to make sure the spacing per line is even (or as even as can be. If an odd number of spaces are added, the extra should be on the left side of where the words are broken)
// (new lines act as spaces, so no spaces on the left/right side of the line)
// (if there is only 1 word on a line, no justification is done to that line)

function justify(str, LL){ // LL=line length(in chars), str is the text input
    if(LL < 2) return; // won't work for strings < 2 chars... can't put a hyphen at the end of a line if it takes a whole line.
    // step 1: break up words longer than 12 chars into 12 chars long and '-' plus the rest of the word
    var words_raw = str.split(' '); // split text into words
    var words = []; // where max length is LL (-1, so we can add '-' for words >LL length)
    var lines = []; // output
    for(var xint = 0, len = words_raw.length;xint < len;xint++){
        var w = words_raw[xint];
        if(w.length > LL){
            var start = 0;
            var step = LL - 1;
            while(start < w.length){
                words.push(w.substr(start,step) + "-");
                start += step;
                step = Math.min(LL - 1, w.length - start);
            }
        }else words.push(w);
    }

    // step 2: go through and keep adding words to the sentence until the total length of the line is 11 (there must be 1 space between words) or less. End lines with a newline char.
    for(var xint = 0, len = words.length;xint < len;xint++){
        // xint is the start point for the next word
        var sentLen = 0; // total chars
        var startWord = xint; // index of first word
        var lastWord = xint; // index of last word
        for(var yint = xint;yint < len;yint++){ // add words until line is full
            sentLen += words[yint].length;
            lastWord = yint;
            if(sentLen >= LL) break; // line full
        }
        sentLen += (lastWord-startWord); // # of spaces = # of words - 1
        
        // line length (sentLen) might be too much now. Remove words until it is short enough
        if(sentLen > LL){
            while(sentLen > 0 && lastWord > startWord && sentLen > LL){
                sentLen -= words[lastWord].length - 1; // - len of word + space
                lastWord--;
            }
        }
        
        // now we have as many words as we can rightfully fit.
        // if the line length is < LL, pad it with spaces to justify.
        var spacesPerWord = []; // len of words in line - 1
        var spacesAdded = 0;
        var finLine = ""; // finished line
        // A: add a value for each word
        for(var yint = startWord, len2 = lastWord;yint < len2;yint++){
            spacesPerWord.push(0);
        }
        // B: go around cyclically, add 1 space to spacesPerWord at that index
        var curIdx = 0;
        var max = spacesPerWord.length;
        if(sentLen < LL){
            while(sentLen + spacesAdded < LL){
                spacesPerWord[curIdx]++;
                spacesAdded++;
                curIdx++;
                if(curIdx >= max) curIdx = 0; // loop around
            }
        }
        // C: create the finished line with words and spaces
        for(var yint = startWord, len2 = lastWord + 1;yint < len2;yint++){
            finLine += words[yint];
            if(yint != lastWord){
                finLine += " "; // normal space between words
                // now add padding spacing for justification
                for(var zint = 0;zint < spacesPerWord[yint-startWord];zint++){
                    finLine += " ";
                }
            }
        }
        // D: store finished line
        lines.push(finLine);
        // E: remember to skip all written words this line so we don't repeat them
        xint = lastWord;
    }

    // step 3: print output
    lines.forEach((line)=>{console.log(line);});
}
console.log('TEST 1:');
justify('hi there', 12);
console.log('============');
console.log('TEST 2:');
justify('I was walking my dog at the park, when my mother arrived.', 12);
console.log('============');
console.log('TEST 3:');
justify('booger brains... Abracadabra\'ing', 12);
console.log('============');

Wednesday, March 15, 2023

Coding Challenge #22

 // Daily Coding Challenge #27
// Determine whether a string of (), [], and {} are properly formatted (enough to match each other, are closed in LOFO order of having been added).
// Return true if it is properly formatted. Return false if it is not.
closerFor = {')':'(',
'}':'{',
']':'['};
function judge(str){
    var stack = [];
    for(var xint = 0, len = str.length;xint < len;xint++){
        var c = str.charAt(xint);
        if(c == '(' || c == '{' || c == '[') stack.push(c); // new phrase
        else if(c == ')' || c == '}' || c == ']'){ // try to close phrase
            console.log('trying close; xint = ' + xint + ', c = ' + c + ', stack len: ' + stack.length + ', stack: ' + stack.toString());
            if(stack.length == 0 || stack[stack.length-1] != closerFor[c]) return false; // incorrect format
            else stack.splice(stack.length-1,1); // successfully closed phrase; pop the back off, LOFO
        }
    }
    console.log('stack len: ' + stack.length);
    if(stack.length == 0) return true; // no phrases left unfinished
    else return false;
}

// tests (PASS means the algorithm works. FAIL means the expected result was not reached)
if(judge("{[{[()]}]}")) console.log("Algorithm PASS"); else console.log("Algorithm FAIL"); // should be true
if(!judge("{[]}]}")) console.log("Algorithm PASS"); else console.log("Algorithm FAIL"); // should be false
if(judge("[][](){}")) console.log("Algorithm PASS"); else console.log("Algorithm FAIL"); // should be true
if(!judge("(")) console.log("Algorithm PASS"); else console.log("Algorithm FAIL"); // should be false

Tuesday, March 14, 2023

Coding Challenge #21

// Daily Coding Challenge #29
// 'Run-length encoding': replace repeated characters with a number before the character.
// Example: "AAABBC" -> "3A2B1C"
// Example: "ZZyaaaa" -> "2Z1y4a"
function encode(str){
    var lastChar = 0;
    var streak = 0;
    var output = "";
    for(var xint = 0, len = str.length;xint < len;xint++){
        var curChar = str.charAt(xint);
        if(lastChar != curChar){
            if(lastChar != 0){ // not the first char
                output += streak;
                output += lastChar;
            }
            streak = 1;
            lastChar = curChar;
        }else streak++;
    }
    if(streak > 0){ // log the last streak/char
        output += streak;
        output += lastChar;
    }
    return output;
}

// tests
console.log('Test #1: ' + (encode('AAABBC') == "3A2B1C" ? ("PASS") : ("FAIL")));
console.log('Test #2: ' + (encode('ZZyaaaa') == "2Z1y4a" ? ("PASS") : ("FAIL")));

Monday, March 13, 2023

Coding Challenge #20

 // based on Daily Coding Challenge #22
// given a dictionary (array of words) and a word (a string) without spaces between words, return all the dictionary words found in the string
// example:
// dictionary: ['cat', 'dog', 'cathode']
// string: cathodedog
// result: ['cat', 'cathode', 'dog']
function getAllStrs(dictionary, str){
    var result = [];
    for(var xint = 0, len = str.length;xint < len;xint++){
        var tmp = str.substr(xint);
        for(var yint = 0, len2 = dictionary.length;yint < len2;yint++){
            if(tmp.indexOf(dictionary[yint]) == 0) result.push(dictionary[yint])
        }
    }
    return result;
}

Sunday, March 12, 2023

Local Livestreamer (Webcam & Mic Test)

Local Livestreamer
  Recording
  ||    

Res:   Scale:

Saturday, March 11, 2023

Coding Challenge #19 Min # of classrooms based on class start/stop times

 // Daily Coding Challenge #21
/*
Given an array of start and stop times for classes, determine by observing where the times overlap how many classrooms would be needed?
Example: [ [1,3], [2,3], [4,5] ] => 2 classrooms needed (indices 1 & 2 are both active during hour 2: 1,2,3 & 2,3)
*/
function calc(ary){
    // we're going to go around and check for each start/stop time whether another start/stop time in the array overlaps with it, counting how many and storing the max.
    var maxOverlaps = 0;
    for(var xint = 0, len = ary.length;xint < len;xint++){
        var overlaps = 0;
        for(var yint = 0, len2 = ary.length;yint < len2;yint++){
            if(xint == yint) continue; // don't compare to itself
            // detecting collision is kinda hard, eh? but detecting lack of collision is easy: if start & stop of A are less than B's start, or A's start & stop are greater than B's stop, it occupies a different range on the one-dimensional plane.
            // for our example, saying A starts at 1 and ends at 2, and B starts at 2 and ends at 3, we will declare this not to overlap, as the bulk of their sessions will not overlap even if the physical students cannot swap places in under a second
            if(!( (ary[xint][0] <= ary[yint][0] && ary[xint][1] <= ary[yint][0])
            || (ary[xint][0] >= ary[yint][1] && ary[xint][0] >= ary[yint][1]) )){
                // failed to not overlap
                // aka overlap
                overlaps++;
                console.log(ary[xint].toString() + " and " + ary[yint].toString() + " overlapped.");
            }
        }
        if(overlaps > maxOverlaps) maxOverlaps = overlaps;
    }
    maxOverlaps += 1; // 1 classroom + 1 classroom for each overlap
    console.log('Number of classrooms needed: ' + maxOverlaps);
}

Friday, March 10, 2023

Coding Challenge #18

 // Daily Coding Challenge #18
// given an array [ary], and a length [k], iterate through [ary] and find the max value of each index and the next [k]-1 values
// example: k=3; [1,2,3,4,5] -> [3,4,5] (max(1,2,3), max(2,3,4), max(3,4,5))
// k=4; [1,5,1,3,1,2] -> [5,5,3]  (max(1,5,1,3), max(5,1,3,1), max(1,3,1,2))
function calc(ary, k){
    for(var xint = 0, max = ary.length - (k-1);xint < max;xint++)
        console.log(ary.slice(xint,xint+k).sort((a,b)=>{return a<b;})[0]);
}
// part of the challenge was to output these as we iterate through the array, without storing them, explaining the choice of form

Thursday, March 9, 2023

Diary #6 Minimalist PHP Proxy

ProxyMe.PHP

Setting:  I want to access sites on a machine that's not connected to the internet, but is connected to a computer running a local webserver. Example: PS4, PS3, or Xbox 360.

Here is the PHP script on GitHub.

The Journey: This was the solution I used to achieve what I needed: nothing complicated, just view a webpage.

Solution: host a PHP page that fetches the webpage from the requested URL and returns it to the requestor.

Problem: Images don't load. Scripts don't execute. CSS isn't loaded. Because asynchronously loaded materials are unavailable through other URLs (can only access our webserver).

Solution: Change all URLs to re-route to our proxy at our webserver. Example: http://cool-pics.com/1.jpg -> http://192.168.1.253/proxyme.php?url=http://cool-pics.com/1.jpg  ,etc... So now when the requesting machine requests the original page 'pics.com' and the page is like <body><img src='http://pics.com/ha.jpg'></body>, then the src of the image will be changed to our webserver with the original src as the query option. Now when the browser tries to load the image, it will ask our webserver, which is reachable and will get the image.

Problem: Images don't appear. Because the MIME type is incorrect.

Solution: Check the file extension in the PHP script and change the MIME type returned for various file extensions. Now when the requesting machine requests the image, our webserver will return metadata that tells the browser what to do with it.

There are more issues I've faced but haven't looked into or been motivated to fix. One foreseeable issue is of course videos. The way the proxy works is by fetching the file from the internet before serving it to the requestor. The ENTIRE file. So for a 100 kB image, no prob, but a 3 GB video?! I believe the saving grace from being a more grave issue is that PHP's timeout should be about 30 seconds by default, and the loading should stop at that time (? unverified), just in case the user unknowingly requests a video with this setup.

Another potential issue: Cloudflare, etc. Because it's not a browser and can't solve challenges that the major browsers are made to solve, even modifying the HTTP user agent wouldn't enable it to work with Cloudflare sites. I don't think there's a quick, convenient way to get around that. A good way to tell if a particular site uses Cloudflare,etc: when you google image search for that site, does the image come up small and blurry, and less than the reported resolution of the image? Cloudflare. I figure Google cached a thumbnail since the original image is unreachable through the usual way images are requested.

Of course, it was intended to just be a one-day personal project, so these limitations are acceptable to me. It accomplished its goal with flying colors.

Proxies are hard.

Wednesday, March 8, 2023

Coding Challenge #17 - Navigate a 2D Matrix Maze

From: Daily Coding Challenge #23

I have been wanting to do something like this... since I read about it today. It makes me want to get into robotics and make them find their way around the house! 

How it works: very basic -- we start with a node at the starting point and it checks clockwise each cardinal direction - up, right, down, left - to see if any of those are the goal node. Meanwhile, the nodes it's checking also check their available nodes clockwise, while keeping a log to not check the same node twice and start an infinite loop.

Below is the test map used in this program. 't' represents 'true'=a walkable path. 'f' represents 'false'=a wall where it can't walk. (Used 'Y' for true and 'L' for false in the code below's console output for legibility)

t f t f     
t t f t    
f t t f    
f t t f 

Starting point is (0,0) and end point/goal is (2,3). As you can see from this test map, we need to go: Down, Right, Down, Right, Down to get to the goal. Or Down, Right, Down, Down, Right. Our program should find both ways to get there. 

Although we defined our maze block in a hard-coded way, it could be redefined and the same structs used for any non-zero sized 2-D matrix of booleans.

// test-compiled @ onlinegdb.com using C++

#include <stdio.h>
#include <iostream>
#include <vector>
#include <string>
using namespace std;

std::string dirFrom4Bit(unsigned char dir){
    if(dir == 8) return "Up";
    if(dir == 4) return "Right";
    if(dir == 2) return "Down";
    if(dir == 1) return "Left";
    if(dir == 0) return "Nowhere";
    return "Unknown";
}

unsigned char flipDir(unsigned char dir){ // for path reporting; path stores the 'from' values but we want directions to get there. (we want 'to right', not 'from left')
    if(dir == 8) return 2;
    if(dir == 4) return 1;
    if(dir == 2) return 8;
    if(dir == 1) return 4;
    return dir;
}

// globals
bool mazeMap[4][4]; // true's are walkable, false's are walls.
bool mazeFound[4][4]; // where we've been, so we don't infinitely loop
int propagationAttempts = 0;
int targetX = 2;
int targetY = 3;
#define maxAttempts 100

struct mazeBounds{
    int x = 0; // lowest index x axis
    int y = 0; // lowest index y axis
    int w = 3; // highest index x axis
    int h = 3; // highest index y axis
}bounds;
struct mazePath{ // for outputting a winning path
    std::vector<unsigned char> dirs; // up/right/down/left, low order 4 bits
    void resetTo(unsigned int index){ // for currentPath: done with child mazePoint, back up to parent by erasing child's entries in this path
        index++; // so it is the necessary length of the vector
        while(dirs.size() > index)
            dirs.pop_back();
    }
};
std::vector<mazePath> paths;
mazePath currentPath; // stay the course, men!
struct mazePoint{
    int x = 0;
    int y = 0;
    int myPathIndex = 0; //
    mazePoint(){}
    mazePoint(int x, int y, unsigned int myPathIndex=0){
        this->x = x;
        this->y = y;
        this->myPathIndex=myPathIndex;
    }
    // dirBit: low-order 4 bits: up/right/down/left: which way we went from the last point to get to this one, so we don't back track
    // return: true if found target down that propagation path, false if not
    bool propagate(unsigned char dirBit){
        if(mazeMap[x][y] == false) return false; // wall, can't be here
        if(mazeFound[x][y]) return false; // been here, done that
        mazeFound[x][y] = true; // don't consider this same point in the future
        if(propagationAttempts >= maxAttempts) return false;
        propagationAttempts++;
        cout << "Try: # " << propagationAttempts << ", Level " << myPathIndex << ", from " << dirFrom4Bit(dirBit) << ", x = " << x << ", y = " << y << endl;
        if(targetX == x && targetY == y){ // this is the target
            // log path to victory
            mazePath p;
            for(int xint = 0, len = currentPath.dirs.size();xint < len;xint++)
                p.dirs.push_back(currentPath.dirs[xint]);
            paths.push_back(p);
            mazeFound[x][y] = false; // allow alternate paths to find the goal
            return true;
        }
        
        bool foundTarget = false; // found target through this path? if so, we'll pass back up true at the end, after trying all paths
        if((dirBit & 8) == 0){ // try up
            if(y > bounds.y){ // can go up
                mazePoint n(x, y-1, myPathIndex+1);
                currentPath.dirs.push_back(2);
                if(n.propagate(2)) // came from...2=below
                    foundTarget = true;
                currentPath.resetTo(myPathIndex);
            }
        }
        if((dirBit & 4) == 0){ // try right
            if(x < bounds.w){ // can go right
                mazePoint n(x+1, y, myPathIndex+1);
                currentPath.dirs.push_back(1);
                if(n.propagate(1)) // came from left
                    foundTarget = true;
                currentPath.resetTo(myPathIndex);
            }
        }
        if((dirBit & 2) == 0){ // try down
            if(y < bounds.h){
                mazePoint n(x, y+1, myPathIndex+1);
                currentPath.dirs.push_back(8);
                if(n.propagate(8)) // came from above
                    foundTarget = true;
                currentPath.resetTo(myPathIndex);
            }
        }
        if((dirBit & 1) == 0){ // try left
            if(x > bounds.x){
                mazePoint n(x-1, y, myPathIndex+1);
                currentPath.dirs.push_back(4);
                if(n.propagate(4)) // came from right
                    foundTarget = true;
                currentPath.resetTo(myPathIndex);
            }
        }
        
        return foundTarget;
    }
    
}entryPoint(0,0);

int main(){
    // setup the maze
    mazeMap[0][0] = true;
    mazeMap[1][0] = false;
    mazeMap[2][0] = true;
    mazeMap[3][0] = false;
    
    mazeMap[0][1] = true;
    mazeMap[1][1] = true;
    mazeMap[2][1] = false;
    mazeMap[3][1] = true;
    
    mazeMap[0][2] = false;
    mazeMap[1][2] = true;
    mazeMap[2][2] = true;
    mazeMap[3][2] = false;
    
    mazeMap[0][3] = false;
    mazeMap[1][3] = true;
    mazeMap[2][3] = true;
    mazeMap[3][3] = false;
    
    cout << "Here's the map:" << endl;
    for(int xint = 0;xint < 4;xint++){
        for(int yint = 0;yint < 4;yint++)
            cout << " " << (mazeMap[yint][xint] ? "Y" : "L"); // print 4 columns, then next columns
            cout << endl;
    }
    
    // clear history of where we've already been
    for(int xint = 0;xint < 4;xint++){
        for(int yint = 0;yint < 4;yint++)
            mazeFound[xint][yint] = false;
    }
        
    if(entryPoint.propagate(0)){
        cout << "Found the goal! In total, there were " << paths.size() << " viable paths. A total of " << propagationAttempts << " propagation attempts were made." << endl << endl;
        
        int shortestPathIndex = 0;
        int longestPathIndex = 0;
        for(int xint = 0, len = paths.size();xint < len;xint++){
            if(paths[xint].dirs.size() < paths[shortestPathIndex].dirs.size()) shortestPathIndex = xint;
            if(paths[xint].dirs.size() > paths[longestPathIndex].dirs.size()) longestPathIndex = xint;
        }
        
        cout << "The shortest path size was " << paths[shortestPathIndex].dirs.size() << " steps, and the longest was " << paths[longestPathIndex].dirs.size() << " steps." << endl << endl;
        
        std::string printPath = "Shortest path steps: ";
        int stepCount = paths[shortestPathIndex].dirs.size();
        if(stepCount > 0) printPath += dirFrom4Bit(flipDir(paths[shortestPathIndex].dirs[0]));
        for(int xint = 1;xint < stepCount;xint++){
            printPath += ", ";
            printPath += dirFrom4Bit(flipDir(paths[shortestPathIndex].dirs[xint]));
        }
        
        cout << printPath << endl << endl << "Wow!" << endl;
        
    }else{
        cout << "Didn't find any paths. " << paths.size() << endl;
    }
    return 0;
}

Tuesday, March 7, 2023

Diary #5 C++ Hex Encoder/Decoder

As part of my Websocket Controller (over-network keyboard input forwarder), I wanted to send the clipboard from the client to the server. To do this with non-ASCII chars, I encoded them as hex. This is copy-pasted from the changes I made this morning, to enable the clipboard to send unicode long strings (wstring), as opposed to the previous single-byte std:::strings.

 (needs #include <string>)
const char *hexChars = "0123456789ABCDEF";
string encodeHex(std::wstring wstr){ // because we can only use std::string as an arg for websocketpp's send(). Encode wstring as hex.
    string o = "";
    cout << "wstring has length: " << wstr.length() << endl;
    for (int xint = 0, len = wstr.length(); xint < len; xint++){
        // high order byte of wchar_t
        unsigned char c1 = (wstr[xint] & 65280) >> 8;
        unsigned char c1_left = hexChars[c1 / 16];
        unsigned char c1_right = hexChars[c1 % 16];
        // low order byte of wchar_t
        unsigned char c2 = wstr[xint] & 255;
        unsigned char c2_left = hexChars[c2 / 16];
        unsigned char c2_right = hexChars[c2 % 16];
        o += c1_left;
        o += c1_right;
        o += c2_left;
        o += c2_right;
    }
    cout << "Finished encoded hex: " << o << endl;
    cout << "It has length: " << o.length() << endl;
    return o;
}

wstring decodeHex(std::string str){ // turn (uppercase) hex string back into wstring
    wstring o = L"";
    cout << "decoding from hex: " << str << endl;
    cout << "hex str len: " << str.length() << endl;
    int len = str.length();
    if ((len & 1) == 1) return L"[Odd num of chars, not hex]";
    if ((len % 4) != 0) return L"[Length wrong for wstring, can't de-hex]"; // need 4 hex letters per wchar_t
    for (int xint = 0; xint < len; xint++){
        unsigned char HI_left; // high order byte of wchar_t, left hex char
        unsigned char HI_right;
        unsigned char LO_left; // low order byte of wchar_t, left hex char
        unsigned char LO_right;

        // char 1, hex 1
        if (str[xint] >= 'A' && str[xint] <= 'F'){
            HI_left = 10 + (str[xint++] - 'A');
        }
        else if (str[xint] >= '0' && str[xint] <= '9'){
            HI_left = str[xint++] - '0';
        }
        else{
            return L"[Char invalid, not hex]"; // only accepting capital A-F & 0-9 as hex
        }

        // char 1, hex 2
        if (str[xint] >= 'A' && str[xint] <= 'F'){
            HI_right = 10 + (str[xint++] - 'A');
        }
        else if (str[xint] >= '0' && str[xint] <= '9'){
            HI_right = str[xint++] - '0';
        }
        else{
            return L"[Char invalid, not hex]";
        }

        // char 2, hex 1
        if (str[xint] >= 'A' && str[xint] <= 'F'){
            LO_left = 10 + (str[xint++] - 'A');
        }
        else if (str[xint] >= '0' && str[xint] <= '9'){
            LO_left = str[xint++] - '0';
        }
        else{
            return L"[Char invalid, not hex]";
        }

        // char 2, hex 2
        if (str[xint] >= 'A' && str[xint] <= 'F'){
            LO_right = 10 + (str[xint] - 'A');
        }
        else if (str[xint] >= '0' && str[xint] <= '9'){
            LO_right = str[xint] - '0';
        }
        else{
            return L"[Char invalid, not hex]";
        }


        wchar_t fin = ((HI_left * 16) + HI_right) << 8; // assemble high order byte
        fin += (LO_left * 16) + LO_right; // assemble low order byte
        wcout << L"___constructed char " << fin << endl;
        cout << "decode progress: " << xint << " / " << len << endl;
        o += fin; // add decoded wchar_t to wstring
    }
    wcout << L"finished wstring: " << o << endl;
    return o;
}

 

 

(needs #include <string> and #include <windows.h>)
/*
void toClipboard(string new_text){
    OpenClipboard(0);
    EmptyClipboard();
    HGLOBAL hg = GlobalAlloc(GMEM_MOVEABLE, new_text.size() + 1);
    if (!hg){
        CloseClipboard();
        return;
    }
    memcpy(GlobalLock(hg), new_text.c_str(), new_text.size() + 1);
    GlobalUnlock(hg);
    SetClipboardData(CF_TEXT, hg);
    CloseClipboard();
    GlobalFree(hg);
}
legacy ASCII version
*/

void toClipboard(wstring new_text){
    OpenClipboard(0);
    EmptyClipboard();
    HGLOBAL hg = GlobalAlloc(GMEM_MOVEABLE, (new_text.size() * 2) + 2); // 2 bytes per char, plus 2-byte null terminating char
    if (!hg){
        CloseClipboard();
        return;
    }
    memcpy(GlobalLock(hg), new_text.c_str(), (new_text.size() * 2) + 2);
    GlobalUnlock(hg);
    SetClipboardData(CF_UNICODETEXT, hg);
    CloseClipboard();
    GlobalFree(hg);
}

/*string fromClipboard(){
    string out = "";
    if (OpenClipboard(nullptr)){
        HANDLE hData = GetClipboardData(CF_TEXT); // CF_UNICODETEXT will only put 1 char into the char *pszText, CF_TEXT will work normally
        if (hData != nullptr){
            char *pszText = static_cast<char*>(GlobalLock(hData));
            cout << "pszText = " << pszText << endl;
            if (pszText != nullptr){
                out = pszText;
            }
            else{
                out = "pszText = nullptr";
            }
            GlobalUnlock(hData);
        }
        else{
            out = "hData = nullptr";
        }
        CloseClipboard();
    }
    else{
        out = "failed to open clipboard";
    }
    return out;
}
// legacy ASCII version
*/

Monday, March 6, 2023

Coding Challenge #16 Distance to nearest vowel

 Distance to nearest Vowel (Edabit)
Given a string, return an array with the distance to the nearest vowel for each character. It searches for the nearest both forwards and backwards.

Example: 'abc' -> [0,1,2]

'abzxciou' -> [0,1,1,2,2,0,0,0]

We'll force lowercase and make non-letter characters return "N/A" in the array.

function doit(str){
    var o = [];
    str = str.toLowerCase();
    
    for(var xint = 0, len = str.length;xint < len;xint++){
        var c = str.charCodeAt(xint);
        if(c >= aCode && c <= zCode){ // is a letter
            // we'll just manually check by looping. use modulo to contain value.
            var leastVowelDistance = 26;
            var forwardsVowelDistance = 26;
            var backwardsVowelDistance = 26;
            var cOffset = c - aCode;
            for(var yint=0;yint<25;yint++){ // forward
                var xchar = alphabet[(cOffset + yint) % 26]; // for upward
                if(xchar == 'a' || xchar == 'e' || xchar == 'i' || xchar == 'o' || xchar == 'u'){
                    forwardsVowelDistance = yint;
                    break;
                }
            }
           
            for(var yint = 0;yint < 25;yint++){ // backwards
                var xidx = cOffset - yint + (yint > cOffset ? 26 : 0);
                var xchar = alphabet[xidx];
               
                if(xchar == 'a' || xchar == 'e' || xchar == 'i' || xchar == 'o' || xchar == 'u'){
                    backwardsVowelDistance = yint;
                    break;
                }
            }
           
            leastVowelDistance = Math.min(forwardsVowelDistance,backwardsVowelDistance);
            o.push(leastVowelDistance);
           
            // debug info, shows the distance forwards and backwards for each letter
            //console.log("Letter = " + alphabet[cOffset] + ", forwardsVowelDistance = " + forwardsVowelDistance + ", backwardsVowelDistance = " + backwardsVowelDistance + ", leastVowelDistance = " + leastVowelDistance);
        }else{
            o.push('N/A'); // not a letter
        }
    }
    
    return o;
}
// note: we're not using 'y' as a vowel here
var vowels = ['a'.charCodeAt(0),'e'.charCodeAt(0),'i'.charCodeAt(0),'o'.charCodeAt(0),'u'.charCodeAt(0)];
var alphabet = [];
var aCode = 'a'.charCodeAt(0);
var zCode = 'z'.charCodeAt(0);
for(var xint=0;xint<26;xint++)alphabet.push(String.fromCharCode(aCode+xint));
function test(str){
    if(str == undefined){ // make random string
        var len = 5;
        str = "";
        var range = zCode - aCode;
        for(var xint = 0;xint < len;xint++)
            str += String.fromCharCode( Math.round(Math.random()*range) + aCode);
    }
    console.log('str: ' + str + '. result: ' + doit(str).toString());
}



test('abc');
test('xyz');
test('cabbage');
test('Spider Man');
test('$ #@ ^*$@ #*');
test('AaAaHhHh');
test('그거양은 체친구입니다.');
for(var xint = 0;xint < 5;xint++) test();

Sunday, March 5, 2023

Coding Challenge #15 - Super Int (u128 JavaScript)

Super Int! (based off of Edabit "Big Integers, Big Errors")

Today we'll create a 128-bit unsigned integer and make some basic operators for it.


Saturday, March 4, 2023

Coding Challenge #14

 Edabit: Jake's Meal Time

// Jake's Meal Time
// Jake eats breakfast at 7am, lunch at 12pm, and dinner at 7pm.
// Using the current time, calculate hours and minutes until Jake's next meal, and specify which meal that is.
// We act like current time's seconds is always 0. We'll calculate to the hour before and then add in the minutes left (if minutes left would be 60, we change minutes left to 0 and add one more 'hour until')

function mealTicket(time){
    // input
    var date = new Date(time);
    var hour = date.getHours();
    var min = date.getMinutes();
    
    // output
    var mealName = "N/A";
    var hoursTil = 0;
    var minsTil = 0;
    
    // logic
    if(hour < 7 || hour >= 19){ // calc for breaky-boo
        mealName = "breakfast";
        if(hour >= 19){
            hoursTil = 24-hour;
            hour = 0;
        }
        hoursTil += 6 - hour;
       
        if(min == 0) hoursTil += 1;
        else minsTil = 60 - min;
    }else if(hour < 12){ // calc for lunch
        mealName = "lunch";
        hoursTil += 11 - hour;
       
        if(min == 0) hoursTil += 1;
        else minsTil = 60 - min;
    }else if(hour < 19){
        mealName = "dinner";
        hoursTil += 18 - hour;
       
        if(min == 0) hoursTil += 1;
        else minsTil = 60 - min;
    }
    
    console.log("Jake's next meal is " + mealName + " in " + hoursTil + " hours, " + minsTil + " minutes. His next meal ticket is for the time: " + date);
}

function test(){ // generate random date
    var n = Math.floor(Math.random() * 4) + 1;
    var op = Math.floor(Math.random() * 4);
    var nowDate = (new Date()).valueOf();
    if(op == 0) mealTicket(nowDate + (n * 1000000));
    else if(op == 1) mealTicket(nowDate - (n * 1000000));
    else if(op == 2) mealTicket(nowDate * n);
    else if(op == 3) mealTicket(nowDate / n);
    else mealTicket(nowDate);
}

mealTicket((new Date()).valueOf());
test();
test();
test();
test();
test();

Friday, March 3, 2023

Coding Challenge #13

Edabit: Return the sum of two numbers

// from the 'Very Easy' page:
simple_sum = (a,b)=>{return a+b};


Now let's take it up one notch by returning a new function that hard-codedly adds the two arguments.

This is just for fun to explore some more of JavaScript.

function make_adder(a,b){
    var fnStr = "function adder(){return " + a + " + " + b + "}";
    window.eval(fnStr);
    try{
        console.log('Using created function, ' + a + ' + ' + b + ' = ' + window['adder']());
    }catch{
        console.log('failed to call function');
    }
}
make_adder(1,2) // expecting 3
make_adder(1,20) // 21
make_adder(13,17) // 30
make_adder(99,1) // 100

interestingly, just eval(fnStr) doesn't work ( i guess because of the function's scope?), but window.eval(fnStr) works as expected. 

Thursday, March 2, 2023

Coding Challenge #12

Daily Coding Problem #13


 // Daily Coding Problem #13
// With integer dLimit and string str, return the longest substring of str that contains at most dLimit distinct characters
// (so the goal is to look for letter duplication sections, they score higher)
// example: dLimit=2, 'aasdf': 'aas' has 2 distinct chars 'a' and 's', while being longest
// example 2: dLimit=2, 'hoooow?' : 'hoooo' or 'oooow', either.
// example 3: dLimit=3, 'hoooow?' : 'hoooow' or 'oooow?', either
function checkStr(str, dLimit){
    var ostr = "";
    for(var xint = 0, len = str.length;xint < len;xint++){ // check each char as a new sequence
        var distinctLeft = dLimit;
        var streakChars = [];
        var tmpStr = "";
        for(var yint = xint;yint < len;yint++){ // go through the sequence and find longest substring with dLimit distinct chars
            var inStreak = false;
            for(var zint = 0, zlen = streakChars.length;zint < zlen;zint++){ // check streakChars to see whether this one is distinct
                var candidate = str.charAt(yint);
                if(streakChars[zint] == candidate){
                    inStreak = true;
                    break;
                }
            }
            
            if(inStreak){ // already in streakChars
                tmpStr += str.charAt(yint);
            }else{ // new char in streak
                distinctLeft--;
                if(distinctLeft < 0) // this char would be too many for dLimit
                    break;
                streakChars.push(str.charAt(yint));
                tmpStr += str.charAt(yint);
            }
        }
        if(tmpStr.length > ostr.length) ostr = tmpStr; // update longest substring found within dLimit distinct chars
    }
    return ostr;
}

function test(str, dLimit){
    console.log('dLimit = ' + dLimit + ', string = \'' + str + '\'. Result: \'' + checkStr(str, dLimit) + '\'');
}
function testRan(){ // generate random strings to test
    var dLimit = Math.floor(Math.random() * 2) + 2; // dLimit will be [2,4] inclusive
    var strLen = Math.floor(Math.random() * 5) + 5; // string length will be [5,10] inclusive
    var aCode = 'a'.charCodeAt(0);
    var testStr = "";
    for(var xint = 0;xint < strLen;xint++) // generate random letters [a,f]
        testStr += String.fromCharCode(aCode + Math.floor(Math.random()*5));
    
    test(testStr, dLimit);
}

test('hahaf', 2);
test('hahaf', 3);
test('doodle', 2);
test('doodle', 3);
test('manhandle', 3);
test('bazooka', 3);
for(var rint = 0;rint < 5;rint++) testRan();

Wednesday, March 1, 2023

Diary #4 LNK 1169, LNK 2005, EnumWindows Example

Working on a simple menu that looks like the PS4's menu. I got LNK 1169 and LNK 2005, and quickly found the cause/fix:

Why does LNK 1169 (and consequently LNK 2005) happen?

It can be a simple code error like 2 different .cpp files each declaring 'int a;', or it could be a project error where file A and B are both 'Source Files', and file A #include's file B.

My case was B. I'd like to quickly log it for myself to help identify and fix it in the future if it escapes me at that time.

The error in the Error List:

Both files test_app.cpp and ps4_menu_sim.cpp are 'Source Files'
 
The file test_app.cpp including ps4_menu_sim.cpp:


 

The fix: right click the file that is included by the other file in Solution Explorer, and click Remove (then click "Remove", and NOT "Delete" when it asks you whether you want to remove it from the project or permanently delete the file).

By my understanding, all 'Source Files' are included by default and it's only in the 'Source Files' that these double declarations mainly occur? Because multiple files can include other dependency files, such as string.h or iostream.h without error. Guess 'Source Files' is best used for OOP classes and globals.

 To avoid the error in the future: instead of right click -> Add on the Source Files tab, right click -> Add on the Resource Files tab. Or just don't add it and it would also be included perfectly well.

 

Also,  an example of EnumWindows, which needs EnumWindowProc passed to it as the first arg. This example stores the found window handles into a windowEnumerator object. More than the visible windows are found by this method (all 'top-level' windows are found by EnumWindows). EnumWindowProc needs to return false to stop enumerating... I'm not really sure how make it return true until I've enumerated enough without checking for a particular window in EnumWindowProc or using global variables top stop after enumerating a certain amount. Since this code returns true indefinitely, it is therefore unusable. Oh well.

#include <iostream>

#include <Windows.h>

#include <string>

class windowEnumerator{
    int len = 2; // indexes available
public:
    HWND *wins = new HWND[2];
    int used = 0; // indexes used/filled
    windowEnumerator(){}
    ~windowEnumerator(){
        delete[] wins;
    }
    bool expand(){
        len *= 2; // double array size
        HWND *newAry = new HWND[len];
        if (!newAry) return false; // couldn't allocate to heap
        for (int xint = 0; xint < used; xint++) // copy over info from old, small array to new array
            newAry[xint] = wins[xint];
        delete[] wins; // free space allocated for old array
        wins = newAry; // make wins point to new array
        return true;
    }
    void add(HWND hwnd){
        if (used >= len && !expand()) return;
        wins[used] = hwnd;
        used++;
    }
    HWND get(int index){
        return (index < used ? wins[index] : nullptr);
    }
    void clear(){
        for (int xint = 0; xint < used; xint++){
            wins[xint] = nullptr;
        }
        used = 0;
    }
}winEnum;
BOOL CALLBACK EnumWindowProc(HWND hwnd, LPARAM lParam){
    winEnum.add(hwnd);
    return true;
}

int main(){
    // let's try something new: enumerate open windows
    if (EnumWindows(EnumWindowProc, NULL)){
        cout << "Successfully enumerated windows this way." << endl;
        cout << "Count of windows found: " << winEnum.used << endl;
        for (int xint = 0; xint < winEnum.used; xint++){
            // get window title bar name
            const int bufferSize = 1024;
            char textBuffer[bufferSize] = "";
            SendMessageA(winEnum.wins[xint], WM_GETTEXT, (WPARAM)bufferSize, (LPARAM)textBuffer);
            std::string str(textBuffer);

            cout << "[ " << xint << " ] ( " << (int)winEnum.wins[xint] << " ): " << str << endl;
        }
    }
    else{
        cout << "Failed to enumerate windows this way." << endl;
    }
    cout << "enter to quit" << endl;
    system("pause");
    //ps4_menu_sim::startup();
    //ps4_menu_sim::main();
    //ps4_menu_sim::shutdown

return 0;

}

Coding Challenge #54 C++ int to std::string (no stringstream or to_string())

Gets a string from an integer (ejemplo gratis: 123 -> "123") Wanted to come up with my own function for this like 10 years ago ...