The DIY Masters

Welcome

My name is Ben Ellison. This page is logging the progress that I have been making towards my goal of becoming a professional software developer. Hopefully it will be useful to others wishing to do the same.

Here is a link to my GitHub.

Connect Four

I recently went to a wedding where they had a giant Connect 4 game for guests to play on during the day. Having a go with it reignited my childhood love of the game so I set to work coding a version up.

My version of the game uses JavaScript to manipulate canvas elements to show the state of play and stores the game state in a two dimensional array. My first version of the game used a single canvas element to represent the board with the frame static and the all of the moves redrawn each time a move was played.


//loop over board array drawing circles
function updateBoardView(board) {
    for (var i=0; i<6; i++) {
        for (var j=0; j<7; j++) {
            if (board[i][j] == 1) {
                drawFilledCircle(cxMoves, {x: j*100, y: i*100}, 100, 35, "red")
            } else if (board[i][j] == -1) {
                drawFilledCircle(cxMoves, {x: j*100, y: i*100}, 100, 35, "yellow")
            }
        }
    }
}

This was fine as a first version as it made the game playable. However, I thought that it would look much better with an animation that gave the same satisfying piece dropping into place visual effect of the real-life game (the clunk is part of that but unsolicited/unexpected website noise is not my favourite). In order to do this I needed to create a different model for the way the moves were drawn - the underlying game logic remains the same.

The new model has two canvases placed on top of each other by changing their z-index style and positioning them absolutely in a wrapper div. The top layer is the frame canvas, which is drawn once as the page loads. The white holes on the frame needed to be transparent in order for the moves to be seen below them. This can be achieved by setting the globalCompositeOperation property of the canvas to "xor" as this means that the filled circles of white drawn onto the black rectangle cancel each other out to leave holes in the canvas through which one can view the moves being animated beneath it.

In order to animate the moves, I decided to extend the model from my bouncing ball (see below) and create Move objects that would identify with a player and would havea predefined destination indicating the level they needed to fall to. When a valid move is played a new Move instance is created and added to a moves array that can be iterated over in order to adjust position and draw the moves with each call to requestAnimationFrame. This has given a much more satifying visual feel and the approach benefits from being able to deal with moves played before the previous animation is finished and still animate correctly.

Below is my latest version of the game - I would love it if you want to check out the code in the sources for this page (connectFour.js and connectFour.css). Having now introduced an animation for the moves my next extension of this project will be to write an AI for it (first job is to write one for the tic tac toe below).

Have a game:

Bouncer

Here is a bouncing ball that I have coded up to bounce around a canvas object at constant speed, bouncing off of the walls as it collides with them. I have implemented this using a Vector type to model the speed and position of a Ball object, updating the ball's position and redrawing each time the animation is repainted using requestAnimationFrame.

Below are the constructors for the Vector type and the Ball type. The position, taken as a parameter when constructing new Balls, is a Vector.


// Vector constructor
function Vector(x, y) {
    this.x = x;
    this.y = y;
}

Vector.prototype.plus = function(other) {
    return new Vector(this.x + other.x, this.y + other.y);
};

Vector.prototype.times = function(factor) {
    return new Vector(this.x * factor, this.y * factor);
};

// Ball constructor
function Ball(position) {
    this.position = position;
    // could randomise this?
    this.speed = new Vector(75, 125);
}

Ball.prototype.act = function(step) {
    var newPosition = this.position.plus(this.speed.times(step));
    if (newPosition.x > canvas.width || newPosition.x < 0) {
        this.speed.x *= -1;
    } else if (newPosition.y > canvas.height || newPosition.y < 0) {
        this.speed.y *= -1; 
    } else {
        this.position = newPosition;
    }
}

The key to giving the Ball movement is the act method which modifies the Ball object's position attribute, using the speed multiplied by the time step between animation frames to calculate where the ball would be after that time has passed. If the new position would take it out of the confines of the canvas that it is bouncing around, then the direction of travel is reversed by mutliplying the speed vector by negative one in the appropriate dimension (x or y) depending on which side of the canvas it will exit from. It will then recalculate the new position on the next frame with the modified speed value.

You may have noticed that the ball slightly overlaps with the side of the canvas on impact as the ball is calculating whether its centre is out of the canvas as opposed to using its edge. With the radius set to a small value, as in this example, this is not a problem as it provides a kind of squish into the walls. It is also more likely to avoid the ball stopping early before the wall if the speed is increased, where it is possible for the ball's centre to project beyond the edge of the canvas without it reaching the edge when calculating the new position, resulting in a turn prior to collision. This could be fixed by calculating the point of collision and updating the position to there before continuing with a reversed speed value on the next animation frame. I will have a look at that in a future post, comparing the two models.

To see the full implementation, check out bouncingBall.js in the sources for this page.

Noughts & Crosses (aka Tic-Tac-Toe)

I have put together a JavaScript version of noughts and crosses. It is currently for two human players and I hope to introduce AI functionality soon.

Have a go:

Using Canvas

Experimenting with Canvas has given me a new way to play with the graphical capabilities of the browser.

Here are a selection of stars with differing numbers of points. They are constructed using the quadraticCurveTo method on the context using the parametric equation of a circle to find points on the outside of the circle at equal distances around the circumference.

I have also written a function that can draw spirals by drawing straight lines between points on a parametrically defined spiral. In order to make the spiral smoother, one must increase the numberOfLines parameter, particularly if there are a larger number of whirls as this will make the curvature more severe.

        
function drawSpiral(context, position, size, whirls, numberOfLines) {
    var x = position.x;
    var y = position.y;
    var angle = 0;
    var totalAngle = whirls * 2 * Math.PI;
    var scale = size / (2 * Math.sqrt(
                        Math.pow(totalAngle * Math.cos(totalAngle), 2) + 
                        Math.pow(totalAngle * Math.sin(totalAngle), 2)
                        ));
    context.beginPath();
    context.moveTo(x + size/2, y + size/2);
    for (var i = 0; i < numberOfLines; i++) {
        angle += totalAngle/numberOfLines;
        context.lineTo(
                        x + size/2 + scale * angle * Math.cos(angle), 
                        y + size/2 + scale * angle * Math.sin(angle)
                        );
    }
    context.stroke();
}

The key to the function fitting within the assigned size limit is using a scale variable to make sure that the spiral has, at its furthest point, a radius equal to half of the size parameter passed to it. The scale is key to allowing the spiral to be resized.

Mouse Trail

Anyone else miss the nineties and early noughties? Me neither.

That is, apart from our old friend the mouse trail. For those of you two young to remember, the mouse trail was the kind of feature only the coolest webpages allowed. Well, nostalgia lovers, I now give you the option to jump in the time machine and enjoy your very own mouse trail.

Linux Troubleshooting

So, I shut down from Linux and went to bed after running some updates from the software manager. Next morning I get up and power up my computer expecting the GRUB boot loader where I choose whether to boot into Ubuntu or Windows. BUT, it doesn't appear! Windows just boots straight up. There must be some mistake, I say to myself. Maybe I missed it, although the default is Ubuntu. Reload. Same. Reload. Same. Oh dear.

So I consult google to ask what to do. It comes back with Boot Repair. So I boot Ubuntu from my USB and follow the standard instructions to install and run boot repair. Restart (with baited breath).

Much to my chagrin, it doesn't really do anything. Several hours later I have finally got the GRUB menu to display after going into BIOS, researching bcdedit, EFI and bootmgr amongst other things and have finally come up with a working solution that feels a bit hacky, although I am still unsure how exactly this problem was caused in the first place.

I suppose the learning point is that the inter-relation of so many different pieces of software inevitably causes bugs or, at least, unexpected consequences. When I was first getting started on my computer learning odyssey I was pleased with the order that could be created inside my toy programs, but now I am coming to realise that the computer as a whole is a mind-bendingly complex beast, with so many interactions between so many pieces of software and hardware that there are almost bound to be so-called 'ghosts in the machine' cropping up now and then. Perhaps there is a simple reason that caused the GRUB menu to disappear but it is not widely shared on community forums if it is.

Project Euler is great

When I was reflecting on my learning so far, I realised that much of my progress had been achieved through the process of rote learning. Whilst, from a pedagogical perspective, this approach can have some merits, I found myself feeling that the best way forward from here would be to start setting myself challenges that were less procedural and would require reflection on how best to utilise my existing skillset to come up with an effective approach.

Project Euler is an excellent resource for honing your computational and mathematical skills through problem solving. The Project Euler problems are great for this and have proved to be challenging and enjoyable in equal measure. I have been using Python for the majority of my solutions - it has been great to combine my love of solving mathematical problems with coding. I have learnt a lot more about python, utilising a wide variety of modules such as itertools and bitarray to help in specific circumstances.

Whilst I am not supposed to share solutions to problems, I thought I could put up a helpful tool for those that are beginning to play around with the problems: a prime finder! This one uses the Sieve of Erastosthenes - it's an oldie but a goodie. There are a variety of other solutions available and many more efficient packages but it is quite satisfying putting your own sieve together. The Sieve of Sundaram is a good next sieve to look at if you are interested in prime sieves.


from math import sqrt
from bitarray import bitarray

def generate_primes(n):
    
    """generate_primes(n)[i] == whether i is prime, given i <= n"""
    
    prime_list = bitarray(n+1)
    prime_list.setall(True)
    prime_list[0] = False
    prime_list[1] = False 
    for i in range(2, int(sqrt(n)) + 1):
        if prime_list[i] == True:
            x = 0
            while i**2 + x*i <= n:
                prime_list[i**2 + x*i] = False
                x += 1 
    return prime_list

if __name__ == "__main__":
    primes = generate_primes(10**9)
    f = open("primes.txt", "w+b")
    f.write(primes.tobytes())
    f.close()

To read your primes.txt file back into your python program use the reader below to generate an array. This will save you lots of time, allowing more efficient solutions to many of the problems. It will also save your computer fan a lot of work and lead to a longer living processor. You're Welcome!


from bitarray import bitarray

def read_primes():
    f = open("primes.txt", "r+b")
    primes_bytes = f.read()
    f.close()
    primes = bitarray()
    primes.frombytes(primes_bytes)
    
    return primes

Whilst I am not quite at the point where I am able to solve the top difficulty puzzles just yet, I have solved 68 so far, which puts me in the top 3.4% of all problem solvers - aiming to get to become a one percenter soon!

Animations

I have been playing with requestAnimationFrame in order to add animations to webpages. The function takes a callback function as a parameter in order to update the animation that you are defining in the next repaint. This callback function will typically call requestAnimationFrame again in order to perpetuate the animation.

Here, I have set the images of Hillary Clinton and Donald Trump at opposite sides of a parametrically defined ellipse. Each time the requestAnimationFrame is called on the animate function, the angle is incremented and this leads to the images moving around the path as the position of the image on the elliptical path is calculated with the angle parameter. I have resized the images of Donald and Hillary using container divs that have been styled (animations.css in sources) to shrink these images down to 100px squares.

Learn Python and C the Hard Way

After getting my feet wet with the codecademy tutorials, I have now decided to start getting busy in the terminal. I have installed Ubuntu onto my computer and am now getting used to package managing, version control and navigating my computer through the command line.

To guide me on my way with this, I have begun using the resources at Learn Python the Hard Way and Learn C the Hard Way, both of which have provided me with insight into areas of the computer I was not previously aware of. These two courses have been really helpful in getting me started using these two programming languages.


from game_functions import *

game_map = Map('room_one')
game = Engine(game_map)
game.play()

Above is the main file for my python riddle game that runs in the command line. Below are the classes that are imported into the game in order to allow it to run.

This exercise was particularly interesting for me as I was able to gain greater insight into how I could get class instances to interact with each other and how the class methods could be used to do things that were not entirely trivial, as had been the case in many of the codecademy tutorials.


from sys import exit 

class Scene(object):

    """The Scene class is the parent class for the rooms in the game."""

    def enter(self):
        print "This should not happen as there should be a subclass."
        exit(1)
         
class Engine(object):

    """The Engine class runs the game and calls enter on the instances of 
    the scenes that the Map creates"""

    def __init__(self, scene_map):
        self.scene_map = scene_map

    def play(self):
        current_scene = self.scene_map.start_scene
        final_scene = self.scene_map.next_scene('finish')
        while current_scene != final_scene:
            next_scene = current_scene.enter()
            current_scene = self.scene_map.next_scene(next_scene)
        current_scene.enter()

class RoomOne(Scene):

    def enter(self):
        print "Welcome challenger. You must use your wits to negotiate your"
        print "way through the rooms of the palace. Good luck.\n"
        print "Ahead lies an open book with pages propped open.\n"
        print "What does man love more than life,"
        print "fear more then death or mortal strife,"
        print "what the poor have the rich require,"
        print "and all contented men desire."
        print "What misers spend and spendthrifts save"
        print "and all men carry to the grave?"
        answer = raw_input('> ')
        while answer!= 'nothing':
            print "Try again, challenger."
            answer = raw_input('> ')
        print "\nVery good. Continue to the next room, challenger.\n"
        return 'room_two'

class RoomTwo(Scene):

    def enter(self):
        print "You enter the second room and an ethereal voice speaks: \n"
        print "One hundred eyes of green and blue" 
        print "Just look like eyes behind me, man."
        print "I shudder, shake and turn to you."
        print "As birds go, I'm your biggest fan.\n"
        print "What am I?"
        i=0
        while i<5:
            answer = raw_input('> ')
            if answer == 'peacock' or answer == 'a peacock':
                print "\nGreat work, challenger. Proceed to the final room.\n"
                return 'room_three'
            else:
                i += 1
                p = 5 - i
                print "Try again, challenger."
                print "You have %s tries remaining" % p
        print "\nNo tries left. Back to the start.\n"
        return 'room_one'

class RoomThree(Scene):

    def enter(self):
        print "This is the final room and now one final puzzle."
        print "A great orb descends from the ceiling."
        print "A voice emanates:\n"
        print "A suit of circling rings I wear;"
        print "Beneath my skin my armour is deep;"
        print "So come and strike me - if you dare!"
        print "For if you wound me, you will weep."
        print "What am I?"
        answer = raw_input('> ')
        if answer == 'onion' or answer == 'an onion':
            return 'finish'
        else:
            print "\nI am afraid your answer has angered the King."
            print "Back to the first room with you.\n\n"
            return 'room_one'

class Finish(Scene):

    def enter(self):
        print "\nCongratulations, challenger."
        print "You have made it out of the court of the Onion King."
        print "Happy riddling!"
        exit(1)

class Map(object):

    """The Map class creates instances of the scenes for the engine to
    call methods on."""
    
    scenes = {
        'room_one': RoomOne(),
        'room_two': RoomTwo(),
        'room_three': RoomThree(),
        'finish': Finish()
        } 
    
    def __init__(self, start_scene):
        self.start_scene = Map.scenes.get(start_scene)
    
    def next_scene(self, next_scene):
        next = Map.scenes.get(next_scene)
        return next

Codecademy is a useful starting point

One of the first things that I have done in my quest to learn programming is try to identify as many high-quality, free resources as possible. Codecademy has been one such resource.

It provides good tutorials for helping beginners get to grips with basic programming concepts such as control flow and provides coverage of a variety of languages.

I have now completed tutorials in a whole host of languages including Python, Javascript, HTML, CSS, Git, SQL, Ruby and Java. One of the key things that one realises is that much of the synctatic structure of the languages is very similar and, in many respects, they are essentially dialects of one another. Conceptualising them in this way has allowed me to become increasingly confident at applying my skills at new areas with greater speed and accuracy.

Of the tutorials offered by codecademy, I would say that the Python and Javascript courses were the most developed in terms of their content and would be the ones that I recommend for new users of the site. Learning to use version control programs like Git is best picked up through incorporating it into your execution of other projects.

A reading list

The Bridge of Death

Here is an interactive game that I created using Javascript on codecademy using switches and boolean logic to mimic a scene from Monty Python's 'Holy Grail'.


function playGame () {
    var user = prompt("Bridgekeeper:'What is your name?'").toUpperCase();

    var elementIsInArray = function(x,xs) {
        return xs.indexOf(x) !== -1;    
    }

    switch (user) {
        case 'ROBIN':
            var quest=prompt("Bridgekeeper: 'What is your quest?'").toLowerCase();
            var capital=prompt("Bridgekeeper: 'What is the capital of Assyria?'").toLowerCase(); 
            if (quest === "to seek the holy grail" && elementIsInArray(capital,["nineveh","assur","nimrud","dur sarukin"])) {
                alert("Bridgekeeper:'Right. Off you go.'")
            }
            else {
                alert("Robin is thrown from the bridge into the gorge of eternal peril")
            }
            break;
        case 'ARTHUR':
            var quest=prompt("Bridgekeeper: 'What is your quest?'").toLowerCase();
            var speed=prompt("Bridgekeeper: 'What... is the air-speed velocity of an unladen swallow?'").toLowerCase(); 
            if (quest === "to seek the holy grail" && speed === "an african or european swallow?") {
                alert("Bridgekeeper:'Huh? I... I don't know that. [he is thrown over by his own spell] AUUUUUUUGGGGGGGGGGGHHH!!'")
            }
            else {
                alert("Arthur is thrown from the bridge and the Bridgekeeper cackles malevolently")
            }
            break;
        case 'LANCELOT':
            var quest=prompt("Bridgekeeper: 'What is your quest?'").toLowerCase();
            var colour=prompt("Bridgekeeper: 'What is your favourite colour?'").toLowerCase(); 
            if (quest === "to seek the holy grail" && colour === "blue") {
                alert("Bridgekeeper:'Right. Off you go.'")
            }
            else {
                alert("Lancelot is thrown from the bridge and the Bridgekeeper cackles malevolently")
            }
            break;
        default:
            var quest=prompt("Bridgekeeper: 'What is your quest?'").toLowerCase();
            var capital=prompt("Bridgekeeper: 'What is the capital of Assyria?'").toLowerCase(); 
            if (quest === "to seek the holy grail" && elementIsInArray(capital,["nineveh","assur","nimrud","dur sarukin"])) {
                alert("Bridgekeeper:'Right. Off you go.'")
            }
            else {
                alert(user + " " + "is thrown from the bridge and the Bridgekeeper cackles malevolently")
            }
    }
}

Init

This is my new blog charting my journey towards becoming a proficient computer programmer. It will also include mathematical curios and other interesting articles that I find.