This is a portfolio of my projects, presented in chronological order.

I provide comments on:

  • What the project accomplishes
  • When it was written
  • How it functions
  • And why it uses certain logic

Not all of my projects are discussed on this page. So stay tuned for more!


Jump to the project which interests you the most.

Map Symbols
Match the Ordnance Survey map symbol shown to the corresponding button
A code-breaking game for two players
Hire Car Maintenance Inc
A graphical user interface application for hire car companies
Pip Calculator
Estimate the time it will take complete divisions in World vs World
Configuration files and scripts for Debian GNU/Linux systems
Learn about what I do


Try the demos from the browser.

Map Symbols Pip Calculator
Screenshot demonstration of Map Symbols Screenshot demonstration of the Pip Calculator

Map Symbols (2016)

Photograph depicting Ordnance Survey maps

Photo by Drew Collins on Unsplash.


My first project, written in the Autumn semester of the 2016/17 academic year. It employs HTML, CSS and JavaScript.

View the source code.

The goal of the game is to match the Ordnance Survey map symbol shown to the corresponding button.

It is licensed using the Apache License 2.0.


Why not play from the browser and see for yourself?

Play Map Symbols
Screenshot of Map Symbols


To win the game, the user must have five correct answers on the scoreboard.

  • The symbol shown must be pseudo-randomly generated

    The Math.Random() function is used to assign a number between 1 and 10 to Num, which is used to select a symbol from imgArray.

    To avoid a symbol being repeated immediately, the value of Num is assigned to PreviousNum. If the values in both variables are equal, RandNum() assigns a new number to Num, until Num and PreviousNum are no longer equal.

  • A star must be “turned on” when a correct answer is given

    A new game begins with all five stars “turned off”. The game is won when they are all “turned on”.

    Each button fires an onClick() event to the CheckAnswer() function, which passes the id variable.

    The values of id and Num are compared:

    • If they are equal, the answer is correct
    • Else, the answer is incorrect
  • A corresponding sound and message should be used whenever an answer is given

    Example of a correct answer:

    The message “Well done!” is displayed

    Example of an incorrect answer:

    The message “The correct answer is…” is displayed

    The sound functionality was provided by the deprecated <bgsound> element, but now uses the modern <audio> element to emulate the same behaviour.


The respective module was taught by Mark Dixon and Liz Stuart, whose approach to teaching made learning to program a joy.

Mastermind (2017/18)


Written in the 2017/18 academic year using C#. Compatible with .NET and Mono.

View the source code.

Mastermind is a code-breaking game for two players.

It is licensed using the Apache License 2.0.


There were two stipulations for the design of this project:

  • Write a console application, not a graphical user interface
  • Avoid features of C# which are not available in C

Screenshot demonstration of playing Mastermind

The user defines the number of positions and colours, then plays against a randomly generated pattern.

The program is resilient to input errors, checking whether values are numeric and within bounds.

The code was developed under the scrutiny of the module leader, who defined what functionality was and was not acceptable. As a result, some C-specific code and quirks are used.

Hire Car Maintenance Inc (2017/18)

Photo by Abhishek Umrao on Unsplash


Written in the 2017/18 academic year, using C# and .NET WinForms.

View the source code.

Hire Car Maintenance Inc serves as an introduction to object-oriented programming.

It is licensed using the Apache License 2.0.


input.txt is parsed to create classes and objects at runtime. Changes made using the interface are saved persistently.

The interface was designed using Windows Forms Designer.

Screenshot demonstration of Hire Car Maintenance Inc

As seen above, the user is able to:

  • Search for companies and cars
  • Add, remove or edit companies and cars

Screenshot demonstration of the search bar functionality in Hire Car Maintenance Inc


Once again, I had the pleasure of learning from Liz Stuart.

Guild Wars 2 Pip Calculator (2018)

A screenshot of Guild Wars 2, depicting three players in the World versus World game mode

© 2021 NCSOFT Corporation. All rights reserved. NCSOFT, ArenaNet, the interlocking NC logo, Aion, Lineage II, Guild Wars, Guild Wars 2: Heart of Thorns, Guild Wars 2: Path of Fire, Blade & Soul, and all associated logos, designs, and composite marks are trademarks or registered trademarks of NCSOFT Corporation. All other trademarks are the property of their respective owners.


For context, Guild Wars 2 is a Massively Multiplayer Online Roleplaying Game (MMORPG).

A reward system was introduced into the game, which awarded player “pips” at five minutes intervals. The more pips a player receives, the faster they chip away at the total number of pips required to complete a tier.

The calculator estimates how long it would take a player to receive the rewards given upon the completion of a tier.

I was motivated to develop the tool as:

  1. I wanted to use the functionality, and be able to share it with my friends

    It turns out that doing these calculations manually is boring. Who knew?

  2. A similar tool had not yet been publicly announced

    The project had its first commit in September 2018, though it was functional from an earlier date.

    Since that time, at least two comparable tools are available. See the Guild Wars 2 Wiki and LimitlessFX.

Much like Map Symbols, this project was written using HTML, CSS and JavaScript.

It is licensed using the Apache License 2.0.

View the source code.


Try it, directly in the browser.

Calculate Pips
Screenshot of the Pip Calculator



Pip Calculator is functional, but not beautiful.

It is naively responsive, using the viewport meta tag, and max-width body property.


The user enters a number of pips, optionally selects a tier to stop at, and is presented with the results.

Input is sanitised:

  • isNumberKey(): The keys pressed must be between 0 and 9
  • isNumberAllowed(): The value must be between 3 and 19

To ensure hours and minutes are displayed with or without plurals as appropriate, the getCaseSwitch() function is used to determine which strings should be used, using the values of hours and minutes.


The total time given is not equal to the additive time of each tier.

Tiers are presented with floored hours and rounded minutes. However, the decimal values of each tier are retained when adding timeToTier to totalTime.

Only after every tier is calculated does totalTime have its hours floored and minutes rounded. This results in the total time being longer.


After playing Guild Wars 2 for thousands of hours, I have something to show for it professionally. Thanks, ArenaNet!

dotfiles (2020)

Artwork depicting the Linux mascot, Tux

Tux Flat is licensed under the GNU General Public License v2.0 or later.


dotfiles was the latest incarnation of my ‘configuration files and scripts for Debian GNU/Linux systems’.

It was designed to accompany a network install of Debian with all tasksel items de-selected.

The project encompasses two years of development in 2020 and 2021, though prior incarnations were available in 2018 and 2019 respectively.

It is licensed using the GNU Public Licence version 3 only.

View the source code.


dotfiles was discontinued after I moved from Debian to openSUSE Tumbleweed.

dotfiles was a moving target throughout development, as I changed tools, improved my understanding of the operating system, best practices within it, et cetera.


I learned to use three types of branches for dotfiles: main, development, and <feature>.

  • main

    Once a milestone had been met, and the files had been tested, a release would be tagged. The tag would then be pushed to main.

  • development

    Add complete features (see below) and bug fixes as atomic commits, ensuring they are trivial to revert.

  • <feature>

    Features were merged into the development branch using the --squash option.

    In the past, I had experienced broken files after committing incomplete features to the development branch. This was problematic, as I lived on the development branch.

    Adopting feature branches largely cured this error in judgement.

Code quality

I endeavoured to make files easy to understand, portable across machines, and use programmatic logic.

As a case study, I will refer to this Polybar launch script from the ArchWiki.

The script performs a naive check to see if the polybar process has been killed:

# Terminate already running bar instances
killall -q polybar

# Wait until the processes have been shut down
while pgrep -u $UID -x polybar >/dev/null; do sleep 1; done

What does this script do, and how can it be improved?

  • Perform a command to search for the string polybar amongst processes running on the system
    • Use a bash-specific variable to check that the process was launched by the current user
  • If the command exits successfully (i.e. the process is still running), wait for one second before issuing the same command again
  • When the command eventually fails, assume that this means the string was not found, and that polybar is no longer running
if pgrep --euid "$(id --user --name)" --exact polybar
    # Kill existing polybar processes
    killall --quiet polybar
    # Wait for the processes to die
    wait $!
  • Improve readability by using GNU long options
  • Check the user id by using a POSIX-compliant command
  • Programmatically wait for the previous command to finish executing

Using the wait built-in solves the problems present in the do-while loop. The if statement can be exited immediately, and there is no reliance on nebulous parsing of another program’s output.

There are many reasons not to parse human-readable output to a script, for example if pgrep:

  • Experiences API or ABI changes
  • Is not installed on the system
  • Has been modified by the distribution maintainer


dotfiles was integral to my learning process for GNU/Linux.

I’m grateful to every single person who collaborated on the documentation I scoured to make dotfiles better. (2022)

Photo by Raman Choudhary on Unsplash.

Synopsis is the latest iteration of my personal website. It is under active development, including content and a fork of the website theme to add more features.

The project uses the Hugo static site generator.

Theme code is licensed under the GNU General Public License v3.0.

Content is licensed under the Creative Commons Attribution-NoDerivatives 4.0 International License. With the exception of the Linux Lab, which uses the GNU Free Documentation License, Version 1.3; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.

View the source code.



I decided to use the Hugo framework for three reasons. To abstract away the design process, simplify content creation, and adopt a mature platform with good documentation.

A content management system such as WordPress is feature-rich, but complex. I don’t need a graphical user interface or WYSIWYG editor, only a theme. The fewer dependencies and less code complexity, the better.


I settled on Gokarna, a theme which is minimal, responsive and themeable.

I’ve made modifications to Gokarna, including pull requests merged upstream:


Thanks to Yash Mehrotra and Avijit Gupta for developing Gokarna.