Microsoft and Chrome

I’ve not long got a new PC and my first Windows 11. As usual, I set it up the way I like it.

Then, I boot up to find Edge has opened all my Chrome tabs.

The weeks go by and I see reports of the same thing on YouTube.

And now today, I’m getting emails from Microsoft asking me to recover my Google account through them.

Hmm

2024 Updates

So it is time for me to setup a new development folder. This time around, unlike the many years in the past, all the test projects will be dumped into a testing folder before they are allowed to join any main stream projects that I am working on.

Now with a PC upgrade I am capable of working a lot faster (8 cores/16 threads helps here), especially with running local AI’s and also working in Linux and MacOS. Most of this will be all on the same machine.

Current projects:

  1. Media server and device management software/server.
    • Video playback
    • Image viewer
    • Devices manual updates by client
    • plus many other features…
  2. Godot Game
    • Terrain generator and texture painting
    • Fauna splatting
    • Various generators to add objects in a scene
    • Miscellaneous player controls (ie. sliding down slopes and climbing walls)
  3. Home AI chat on the go.
    • Home AI that runs on a server that allows me to chat over the internet using my phone
    • Text or speech input.
    • Optional text to speech output
    • Selection of local LLM’s (ie Llama chat, or Mistral Instruct)

With WSL in windows it does make it easier to develop and test Linux software, however it is not that good because it doesn’t give raw access to hardware. This is not a problem for development and testing.

There will be a lot more to come over the months as things start to get organised and time is given to these projects.

Catch you all soon…

Carl

Testing before an interview

It’s extremely important if you are taking a laptop to an interview to showcase a project that you’ve been working on to thoroughly test it before going. I found this out recently.

It was a Node JS project I had been working on for about six months, it loaded up, built and ran fine the night before and even on the morning of the interview. Doubts that anything could go wrong just didn’t exist. This project was working.

But that wasn’t the case in the end.

During the interview I booted up, loaded the project in as I had been doing, no changes to the system or project. Then I ran the project and opened up the page in a web browser.

Garbage! That’s what I got on the screen. Just garbage!

According to the output log from the project it was all going fine, but that’s not what was on the screen in the browser. I could see some of the text and icons but they were all squashed up on the screen. Argh! I thought. How could this happen?

I had to resort to just explaining the project and apologising for the failure for it to work properly.

Interview over and headed back home on the train. Furious at myself for this to happen.

Whilst on the train, I opened up my laptop to write a thank you email for the interview which I had to connect to my phones hot spot. Email sent, I decided to open up the project to quickly see why it all went wrong. The project ran, I opened it up in the browser… And it just worked!

After a very short investigation I found what the problem was. A CSS file was being requested from a CDN (content delivery network) which when connected to the internet could get access to and use in the project. During the interview, I did not connect to the internet and the CSS file was not loaded into the page. No layout information, colours, nothing…

So that was it. No internet. Project cannot access that single CSS file.

Of course now I’ve added the CSS file directly to the project and it does work completely offline.

Lesson learned.

ChatGPT vs Google Bard: AI programming in C++

Bard fails miserably at recursive programming where ChatGPT gave an almost finished result.

I asked both to write a C++ program given a folder to monitor its contents and recurs down through all sub folders and monitor those files too.

At the moment, Bard hasn’t got a grip on recursive programming. ChatGPT came out the clear winner. Although I did have to coax it to get it to include the right header files.

The final result definitely wouldn’t be production worthy but a good start to show how to work on this particular task type.

#include <iostream>
#include <sys/inotify.h>
#include <unistd.h>
#include <cstring>
#include <vector>
#include <dirent.h>

#define EVENT_SIZE (sizeof(struct inotify_event))
#define EVENT_BUF_LEN (1024 * (EVENT_SIZE + 16))

void monitorDirectory(const char* directory, int fd, std::vector<int>& watchDescriptors) {
    // Add a watch for the specified directory
    int wd = inotify_add_watch(fd, directory, IN_MODIFY | IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO);
    if (wd < 0) {
        std::cerr << "Failed to add watch for directory: " << directory << std::endl;
        return;
    }

    watchDescriptors.push_back(wd);

    // Scan subdirectories
    DIR* dir = opendir(directory);
    if (!dir) {
        std::cerr << "Failed to open directory: " << directory << std::endl;
        return;
    }

    struct dirent* entry;
    while ((entry = readdir(dir)) != nullptr) {
        if (entry->d_type == DT_DIR) {
            if (std::strcmp(entry->d_name, ".") != 0 && std::strcmp(entry->d_name, "..") != 0) {
                std::string subdirectory = std::string(directory) + "/" + entry->d_name;
                monitorDirectory(subdirectory.c_str(), fd, watchDescriptors);
            }
        }
    }

    closedir(dir);
}

int main() {
    int fd = inotify_init();
    if (fd < 0) {
        std::cerr << "Failed to initialize inotify" << std::endl;
        return 1;
    }

    const char* directory = "/home/wlgfx/Downloads";
    std::vector<int> watchDescriptors;
    monitorDirectory(directory, fd, watchDescriptors);

    std::cout << "Monitoring changes in directory: " << directory << std::endl;

    char buffer[EVENT_BUF_LEN];
    while (true) {
        int length = read(fd, buffer, EVENT_BUF_LEN);
        if (length < 0) {
            std::cerr << "Failed to read events" << std::endl;
            break;
        }

        int i = 0;
        while (i < length) {
            struct inotify_event* event = (struct inotify_event*)&buffer[i];
            if (event->len) {
                if (event->mask & IN_CREATE)
                    std::cout << "File created: " << event->name << std::endl;
                else if (event->mask & IN_DELETE)
                    std::cout << "File deleted: " << event->name << std::endl;
                else if (event->mask & IN_MODIFY)
                    std::cout << "File modified: " << event->name << std::endl;
                else if (event->mask & IN_MOVED_FROM)
                    std::cout << "File/Folder moved from: " << event->name << std::endl;
                else if (event->mask & IN_MOVED_TO)
                    std::cout << "File/Folder moved to: " << event->name << std::endl;
            }
            i += EVENT_SIZE + event->len;
        }
    }

    // Cleanup
    for (int wd : watchDescriptors)
        inotify_rm_watch(fd, wd);

    close(fd);

    return 0;
}

Perfect Maze now working

After a thorough going over of the code, I’ve managed to get this one working. Although painfully slow, at least it works. In the future I will be looking at other algorithms which are much faster. I only remembered this one from a long time ago so I just thought I’d have a go on my own without any help with implementing it.

// File: generate.agc
// Created: 19-09-21

/*
	Attempting to generate a maze using a decent algorithm.
	Unfortunately this particular algorith is painfully slow,
	but for the time being, it does work.
*/

type MAZE
	seed as integer
	wid as integer
	hgt as integer
	maze as integer[0,0]
endtype

function maze_get_odd_random(max as integer)
	val = random(0, ((max - 2) / 2)) * 2 + 1
endfunction val

function maze_not_visited(obj ref as MAZE, xpos, ypos)
	val = obj.maze[ypos, xpos]
endfunction val

/*
	The width and height have to be even numbers.
	NB: In AGK this means 0 to width (as even) for example
		NOT 0 to width - 1
*/

function maze_gen(obj ref as MAZE, wid as integer, hgt as integer, seed as integer)
	obj.seed = seed
	
	SetRandomSeed(seed)
	
	obj.wid = wid
	obj.hgt = hgt

	/*
		Wierd way of assigning a multidimensional array
		Assign the first dimensions length
		And then assign the length of each separate part
		ie.
		a[0].length = 5 (giving a[0,4])
		is not the same as
		a[1].length = 10 (which gives a[1,9])
		
		Also, AGK arrays end with the value assigned.
		ie. length is 10 so the array goes from 0 to 10
		instead of 0 to 9.
	*/
	
	obj.maze.length = hgt

	for c = 0 to hgt
		obj.maze[c].length = wid
		
		for x = 0 to wid
			obj.maze[c, x] = 1
		next
	next c
	
	/*
		If I remember correctly, I choose a random cell.
		But, it has to be, erm, ODD or EVEN???
		I will find out...
		
		It has to be odd.
	*/
	
	currentx = maze_get_odd_random(wid)
	currenty = maze_get_odd_random(hgt)
	
	// set position to 0
	
	obj.maze[currenty, currentx] = 0
	
	done = 0
	
	repeat
		
		// iterations to run through (might increase)
		for route = 0 to 63
			
			/*
				Basically direction is UP, DOWN, LEFT or RIGHT
			*/
			
			direction = random(0, 3)
			
			select direction
				case 0:
					if currenty - 2 > 0
						if maze_not_visited(obj, currentx, currenty - 2)
							obj.maze[currenty - 2, currentx] = 0 // clear path
							obj.maze[currenty - 1, currentx] = 0 // between
						endif
						dec currenty, 2
					endif
				endcase
				
				case 1:
					if currenty + 2 < hgt
						if maze_not_visited(obj, currentx, currenty + 2)
							obj.maze[currenty + 2, currentx] = 0
							obj.maze[currenty + 1, currentx] = 0
						endif
						inc currenty, 2
					endif
				endcase
				
				case 2:
					if currentx - 2 > 0
						if maze_not_visited(obj, currentx - 2, currenty)
							obj.maze[currenty, currentx - 2] = 0
							obj.maze[currenty, currentx - 1] = 0
						endif
						dec currentx, 2
					endif
				endcase
				
				case 3:
					if currentx + 2 < wid
						if maze_not_visited(obj, currentx + 2, currenty)
							obj.maze[currenty, currentx + 2] = 0
							obj.maze[currenty, currentx + 1] = 0
						endif
						inc currentx, 2
					endif
				endcase
			endselect
		next
		
		/*
			Print this mazes iteration (just for testing)
		*/
		
		Print( ScreenFPS() )
		maze_print(obj)
		print("Calculating...")
		sync()
		
		/*
			Check if maze is done (the silly bit)
		*/
		
		done = 0
		
		for h = 1 to hgt - 1 step 2
			for w = 1 to wid - 1 step 2
				if obj.maze[h, w] = 1
					done = 1
					exit // quick exit if not done
				endif
			next
			if done = 1 then exit
		next
		
	until done = 0
	
endfunction obj

function maze_print(obj ref as MAZE)
	print (obj.wid)
	print (obj.hgt)
	//print (random(0, 2) - 1) // HUH
	
	for h = 0 to obj.hgt
		s as string = ""
		for w = 0 to obj.wid
			if obj.maze[h, w] = 1 then s = s + "#" else s = s + " "
		next
		print (s)
	next
endfunction

Hmm… Almost…

// File: generate.agc
// Created: 19-09-21

/*
	Attempting to generate a maze using a decent algorithm
*/

type MAZE
	seed as integer
	wid as integer
	hgt as integer
	maze as integer[0,0]
endtype

function maze_get_odd_random(max as integer)
	val = random(0, max / 2) + 1
endfunction val

function maze_not_visited(obj as MAZE, xpos, ypos)
	val = obj.maze[ypos, xpos]
endfunction val

/*
	The width and height have to be even numbers.
	NB: In AGK this means 0 to width (as even) for example
		NOT 0 to width - 1
*/

function maze_gen(obj ref as MAZE, wid as integer, hgt as integer, seed as integer)
	obj.seed = seed
	
	//SetRandomSeed(seed)
	
	obj.wid = wid
	obj.hgt = hgt

	/*
		Wierd way of assigning a multidimensional array
		Assign the first dimensions length
		And then assign the length of each separate part
		ie.
		a[0].length = 5 (giving a[0,4])
		is not the same as
		a[1].length = 10 (which gives a[1,9])
		
		Also, AGK arrays end with the value assigned.
		ie. length is 10 so the array goes from 0 to 10
		instead of 0 to 9.
	*/
	
	obj.maze.length = hgt

	for c = 0 to hgt
		obj.maze[c].length = wid
		
		for x = 0 to wid
			obj.maze[c, x] = 1
		next
	next c
	
	/*
		If I remember correctly, I choose a random cell.
		But, it has to be, erm, ODD or EVEN???
		I will find out...
		
		It has to be odd.
	*/
	
	currentx = maze_get_odd_random(wid)
	currenty = maze_get_odd_random(hgt)
	
	// set position to 0
	
	obj.maze[currenty, currentx] = 0
	
	done = 0
	
	repeat
		
		// iterations to run through (might increase)
		for route = 0 to 3
			
			/*
				Basically direction is UP, DOWN, LEFT or RIGHT
			*/
			
			direction = random(0, 3)
			
			select direction
				case 0:
					if maze_not_visited(obj, currentx, currenty - 2) and currenty - 2 > 0
						obj.maze[currenty - 2, currentx] = 0 // clear path
						obj.maze[currenty - 1, currentx] = 0 // between
						currenty = currenty - 2
					endif
				endcase
				
				case 1:
					if maze_not_visited(obj, currentx, currenty + 2) and currenty +2 < hgt
						obj.maze[currenty + 2, currentx] = 0
						obj.maze[currenty + 1, currentx] = 0
						currenty = currenty + 2
					endif
				endcase
				
				case 2:
					if maze_not_visited(obj, currentx - 2, currenty) and currentx - 2 > 0
						obj.maze[currenty - 2, currentx] = 0
						obj.maze[currenty - 1, currentx] = 0
						currentx = currentx - 2
					endif
				endcase
				
				case 3:
					if maze_not_visited(obj, currentx + 2, currenty) and currentx + 2 < wid
						obj.maze[currenty, currentx + 2] = 0
						obj.maze[currenty, currentx + 1] = 0
						currentx = currentx + 2
					endif
				endcase
			endselect
		next
		
		/*
			Check if maze is done (the silly bit)
		*/
		
		for h = 1 to hgt - 1 step 2
			for w = 1 to wid - 1 step 2
				if obj.maze[h, w] = 1
					done = 1
					exit // quick exit if not done
				endif
			next
			if done = 1 then exit
		next
		
	until done = 0
	
endfunction obj

function maze_print(obj ref as MAZE)
	print (obj.wid)
	print (obj.hgt)
	print (random(0, 2) - 1) // HUH
	
	for h = 0 to obj.hgt
		s as string = ""
		for w = 0 to obj.wid
			if obj.maze[h, w] = 1 then s = s + "#" else s = s + " "
		next
		print (s)
	next
endfunction

Not quite working… Need a nap… Not well…

A Perfect Maze

So I got an algorithm done last week that produced a perfect maze. Unfortunately though, I wiped my 2 x 1Tb SSD’s on my PC and didn’t back up the code.

So I am starting again.

(I’m not too good by the way, so I am documenting this)

I didn’t want a recursive algorithm because I think that on low end architecture this would be bad for memory usage. This meant that the algorithm would possibly be slower. For a 40 by 40 maze it would still be instant, even though there was a silly check in there.

Here goes. (back soon with some snippets)

I’m using AGK and will port everything to C.

Back to these C++ tuples

Okay, now that Christmas and New Year is finally over and I’ve got pockets like a white eared elephant, I decided to have another blast at these tuple things because they were doing my head in.

All along I’ve been thinking that a class/struct would be much better. In most cases I would be right, but then I thought, “Hang on, this is C++. Let me check if I can return a tuple from a function.”

Hey, turns out I could.

#include <cstdlib>
#include <iostream>
#include <tuple>

using namespace std;

auto get_tuple(string name) {
    return make_tuple(name, 1);
}

int main(int argc, char** argv) {
    auto t3 = get_tuple("FOO");
    auto t4 = get_tuple("BAR");
    
    cout << get<0>(t3) << endl << get<0>(t4) << endl;
    
    return 0;
}

And voila, just like that I can now return multiple values from a function.

The one thing you have to be careful of is the setup. A typo mistake or a type mistake can and will cost hours of debugging so be wary of things like this:

    auto t1 = std::make_tuple("foo", 1, 2);
    auto t2 = std::make_tuple("bar", 1.0, 2);

Now even if I added to the first tuple “foo”, 1f, 2, this would not match the second as the second tuple uses a double value. A pain for debugging.

Just the fact you can return multiple values of data without having to define a complete struct or class is actually quite a nice addition.

I have done some research into tuples now and found that these use cases are good, but for larger data, it is so much better even to still use a struct/class because with the above examples you still are having to use index values to access the data items. Where as a struct/class, the data items are named.

Although this however can be overcome by using enums, larger tuples are very likely better off being converted to a struct/class.

Even after 35+ years of programming I’m still learning.

Time to move onto something else… Bigger…

Some C++ tests

Well, after a while of faffing about with other things, I thought I’d go through some of the new features of C++17 and just a quick play about I found some very interesting new features.

#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
#include <tuple>

using namespace std;

int main(int argc, char** argv) {
    vector<string> strings = {
        "Hello",
        "World",
        "This",
        "is",
        "the",
        "best",
        "I",
        "can",
        "do"
    };
    
    for (string str: strings) { // I now know
        cout << str << endl;
    }
    
    cout << strings.size() << endl;
    
    // https://www.geeksforgeeks.org/tuples-in-c/
    // makes it more interesting with tuple_cat
    
    auto mytuple1 = make_tuple("triangle1", 't', 10, 15, 20); // interesting...
    auto mytuple2 = make_tuple("triangle2", 'b', 1, 2, 3);
    cout << get<0>(mytuple1) << endl;
    mytuple1.swap(mytuple2);
    cout << get<0>(mytuple1) << endl;
    cout << tuple_size<decltype(mytuple1)>::value << endl;

    return 0;
}

First off the std<vector> initialisation can now be done as it is with Java, but more interestingly is the for loop which works with any class list. That makes life so much easier for C++ handling data.

The next bit was the tuples. For some reason I always associated tuples with a set of 3 items. But after this… This allows for a quick, and maybe dirty way, of avoiding setting up a new class to handle short bursts of data. Very simple.

Now I’m going to play around with some more C++17 features. I may end up moving completely back to C++ and away from Java again.

EDIT:

The output:

Hello
World
This
is
the
best
I
can
do
9
triangle1
triangle2
5

RUN FINISHED; exit value 0; real time: 0ms; user: 0ms; system: 0ms

Java ‘long’ to C++ ‘int64_t’

Hallelujah!

For the last two weeks I’ve had an intermittent problem with my C++ version of the crypto socket client, and tonight I finally cracked the issue.

The Java version is using ‘long’ for doing it’s calculations and the C++ version was using a 64 bit integer, which should have been fine until tonight I realised that Java was using its ‘long’ values as signed. Whereas I had been using ‘uint64_t’ in the C++ version.

This caused the C++ version to work once in every 5 to 10 tries. Just by removing the unsigned, it now works flawlessly.

Phew! Now I can blitz the rest…

(Yep, it has been a while since the last post)