My Grades Depend on 2 Weeks of Golang.

Chig Beef - Aug 20 '23 - - Dev Community

Introduction

I used Golang for my big school project after mere weeks of use and this is how easy it was, and the issues I came across.

The client and the Application

In my Digital Tech class I needed to make an application for my final project which is worth 30% of my grade. I gravitated towards a natural selection simulation, I enjoyed creating them and had many failed ones in may past, so I asked my Biology teacher if they'd like one for educational purposes, and they agreed.
At this point I knew what I was making, and who I was making it for, just not how I was going to make it.

Picking Golang

I know a few languages, Python, JavaScript, C#. I learnt these languages from school, so everybody in my class knew these languages.
C# would've been the first obvious choice, most of the class was using this because we had used it most recently, but I decided not to.
Python would've been a great choice, I had experience writing neural networks previously in this language, but that would just be too easy.
Even JavaScript was fine, using JavaScript would've made it easy to distribute my code to students on our school laptops, which were littered with the school's software (malware) that blocked everything.
I chose none of these, I chose the language I had only started learning 2 or so weeks prior. I had heard of Golang's simplicity, performance, and all its other praise, so putting it to the test by putting my grades on the line seemed like a great idea in the end.

How it started

In the first phase of the project (after my atrocious designing and mockups) I came to a quick halt, how are the students going to use my program?
You see, at my school you can't run executables, you need special permission, or the executable has to be distributed by the school.
I could distribute my code via the school, but to get every Biology student to install an executable (and most of them don't even know what that means) could be a big ask.
The other option I had was to ditch Golang and go for JavaScript. Everybody has a browser, so everybody should be able to use it. However, I knew the memory and speed issues with JavaScript weren't going to match well with this project, it was very unlikely to work.
My last option I thought I had was to compile Golang to WebAssembly (WASM). This was working alright, but I kept running into performance issues, and this was unacceptable for a program that would be making calls into WASM at a high frequency.
As a joke idea I also suggested to my client that I could make a server in Golang, and then communicate to that server from a JavaScript client, which would be on a student's laptop.
I chose the last option.

Setting up the server

I had never communicated to the browser like this in any other language, so this was a crazy idea to do it in a new one. It took me a while, but using the philosophy of "scrap your prototypes," I made a graveyard, until I could write myself a server. I also made a frontend in JavaScript to test requests on the server. Eventually, I wrote a server that could send over a homepage (html, css, js) and could execute functionality via "GET" requests.
Here is my main function.

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/sim/{id}", takeRequest).Methods("GET")
    r.HandleFunc("/start/{id}", startSimulation).Methods("GET")
    r.HandleFunc("/remove/{id}", removeSimulation).Methods("GET")    
    r.PathPrefix("/").Handler(http.FileServer(http.Dir("../Frontend"))) 
    http.Handle("/", r)
    http.ListenAndServe(":9090", nil)
}
Enter fullscreen mode Exit fullscreen mode

This was all I needed; it was pretty simple to set up.

Learning how to make a Machine Learn

The next step was to connect this to a bunch of animals with neural networks that could interact with each other. And honestly, it wasn't that hard.
From my experience writing similar simulations I was able to smash out the boilerplate, writing up structs (I had never used structs before, just classes, this is important later) for things like nodes and layers, creating blank brains, and also assigning random weights to links between nodes. I eventually came across my first issue, and it made me realise why Python is used for machine learning, even though it's infamously slow at loops.
In a neural network, inputs connect to hidden nodes, hidden nodes connect to more hidden nodes for a few layers, and then the last layer connects to outputs. This was an issue, because hidden nodes connect to both hidden nodes, and outputs, an issue that didn't matter in a dynamically typed language. I couldn't put outputs and hidden nodes in the same variable. As a quick fix I just made 2 variables and populated one of them, and if the node is from the last layer I populate the other one. Not proud of that one, but definitely something I need to go back to at some point.
Here is the hidden node struct:

type Node struct {
    num       float32
    linksN    []Node
    linksO    []Output
    fired     bool
    infs      int
    weights   []float32
    lastLayer bool
}
Enter fullscreen mode Exit fullscreen mode

num is the sum of data the node gets from the nodes in the layer above.
linksN and linksO is my terrible solution to the static typing problem.
fired is just a check as to whether num is greater than 0.
infs is how many nodes added values to this node
weights is a parallel slice to linksN and linksO that is a modifier value to pass down to other nodes.
And lastLayer is simply whether this node is in the last layer, also part of my terrible solution.

It's not updating!!

Now I was pretty far into my project, it had been going great using Golang, sure, it was different to many of the languages I was used to, but it was very nice to write. I was at the point of moving my sheep around the simulation, the code was simple enough, add a value to the coordinates of the sheep. So I did this in a function, and... it didn't move. I checked whether its coordinate was changing within the function (print debugging of course), and it was, my confusion was increasing. I checked if the value was changing right before and after the function call, and it was not. Now, I had never touched a pointer in my life, I had heard about them, knew they were dangerous, and went back to living in my little Python world, but as soon as I had this issue, I knew the problem was because of references. It took little effort to fix this, just turn the slice (list) of sheep into a slice of pointers to sheep, and it worked! Now my sheep could move all over the screen!

type State struct {
    allGrass  []*Grass
    allWolves []*Wolf
    allSheep  []*Sheep
}
Enter fullscreen mode Exit fullscreen mode

It's all pointers, and works.

"I want to save my progress" - Client

So I knew how to save data to a file in Golang, but how do I allow the teacher to save the simulations if their computer is busy running the simulations? Simple, go.
It's crazy the solution was this simple, I just put a forever loop in a function that could listen for a range of commands, including quit, load, and save, and when I called the function, I just had to write go before it. This is an insanely good feature, and brought my project up to a whole new level alone.

go runCommand()
Enter fullscreen mode Exit fullscreen mode

This is all the code you need to see, so simple.

Afterthoughts

Golang is an incredible language, probably my favourite now. The fact that I was not only able to create a server for the first time, but also implement a neural network, with server-side commands concurrently all in a language I barely knew is phenomenal

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .