Monday 25 July 2016

The Web Dev Budget Experiment (Part 3/3)

The month of June went by. By the end of it, my monthly personal expenses amounted to a grand total of 1,0007.55GSD. Looks like I blew my budget by 7.55SGD. Which amounts to maybe 5 cups of coffee.

So what's SEF?

SEF stands for Scrooge Emergency Fund. See, that column that says 2SGD every damn day? At 2SGD a day, that's 712SGD a year I put aside. What am I gonna do with all that money? I dunno. Take a vacation? Buy that new laptop? Bring my parents out for a nice dinner? Pay more of my mortgage?

Money doesn't spring from nowhere. Whatever you don't spend, is whatever you have left. Pretty obvious, isn't it?

Many of my friends earn more than I do, but somehow I'm the go-to guy for emergency loans. Not because I'm better at earning money, but because I'm better at not spending it. Go figure.

The Financial Finale

Yes. it is possible to survive on 1,000SGD a month. The best thing about it all? I didn't break a sweat doing it. I wasn't even trying particularly hard. I was even able to get sick, and pay for medical attention.

But of course, there'll be the usual protestations...

What if you get really sick? Like, hospitalized?

What if I get cancer? Or struck by lightning? Or run over by a Lamborghini? I mean, we're talking about extraordinary circumstances here. Come on, really? If you expect your monthly expenditure to cover extraordinary circumstances, 10,000SGD a month wouldn't be enough. And most people in sunny Singapore don't earn half that.

What about family? Kids?

What about a pet dog? Cats? A goldfish? Look, this is about personal expenditure. What it takes to keep you going. Don't give me crap about spending on family, wife (or wives), kids and shit. Don't bring anyone else into it. Personal expenditure, got that?

What about entertainment?

Hey, I watched movies! In fact, I watched two movies!

I tinkered around with my code, did research online and in the library. That happens to be free... or at least dirt-cheap. Oh, that doesn't sound entertaining to you? That's because you're not an awesome tech geek. You have expensive tastes. How is that my problem?

Your lifestyle determines how much you need

A friend once commented that I am an expert on the minimum amount required to survive. I beg to differ. I am an expert on the minimum required amount for me to survive. Happily.

Everyone's personal situation is different. Some people can't live without their air-conditioned restaurants, Starbucks coffee and Prada handbags. Some people will die if they don't spend money at bars and flirt with girls on weekends.

That's fine. It's all good.

But if you can't do it, don't assume no one else can do it. Likewise, this is how I survive on 1,000SGD a month, or less. I don't expect everyone to be able to do it, just because I can. In order for you to live the way I do (and like it), you would have to be me. And honestly, if everyone lived like me, Singapore's economy would grind to a halt.

You see, as a web developer, I don't earn a lot. But there is one saving grace. I have zero appreciation for the finer things in life. I can't tell red wine from Ribena. I don't know, and don't want to know, whatever the hell blue cheese is. And as for cigars, I'd prefer to kill myself using cheap tobacco. And I fucking hate hanging out in Orchard Road.

Spartan? I'll show you Spartan.

Some friends have commented on my spartan lifestyle. Sorry, guys. I don't dine in hell and there's no way I'm donning a shield and loincloth and uttering cheesy lines while kicking people into bottomless pits. The point is, this isn't spartan to me. I don't feel the least bit uncomfortable living this way.

And still...

...we have the oddballs screaming about how the cost of living is going up, how the foreigners are taking the jobs and how they used to be able to get high-paying jobs and go for holidays, and other whiny shit like how Singapore is the most expensive city in the world.

Bitches, please. In tech parlance, 404 Error: Fucks Not Found.

You wankers were lucky to be overpaid for your services at a time where there was no better and cheaper talent around. And instead of preparing for the day you would no longer be that fortunate, you chose to get accustomed to a lifestyle you didn't have the ability to sustain. You want expensive holidays and nice things? Stop griping and go earn some money instead.

Just my 2SGD worth. You can take that to the bank!
T___T

Saturday 23 July 2016

The Web Dev Budget Experiment (Part 2/3)

So here goes nothing. We have a day-to-day accounting from the figures I pulled from the Scrooge Expense Tracker.

To make these figures easier to digest, I've split them into intervals of 10 days, coinciding largely with the waypoints I stated earlier. Along the way, I'll explain certain things that may not make much sense to the casual bystander.

Food

This covers meals I eat outside, or groceries I purchase to prepare meals. My groceries  are usually wholemeal bread, eggs, baby butterhead lettuce, margarine, cheese, ham, tomatoes, bananas and bottled water. I may even occasionally spend money on a bag of tortilla chips or cookies.

That's a typical three-meal set
for a day at the office - three
sandwiches, two tomatoes and two
bananas.

Cigarettes

I smoke slightly less than a pack a day, so that roughly equates to me buying 9 packs every 10 days. How much I spend on the pack really depends on where I am at the moment - because different places sell at different prices.

I really should quit, but then I'd have totally no vices and that would be seriously depressing.

SEF

It's two dollars a day. What's SEF stand for? You'll find out later.

Others

This column is for expenses that crop up from time to time. For example, topping up of my EzLink card is pretty regular, but not regular enough to warrant its own column. The occasional miscellaneous purchase goes here.

Day 1 to 10

Day Food Cigarettes SEF Others Total Comments
1 4.50 10.20 2.00 0.00 16.70
2 12.10 10.00 2.00 10.00 34.10 Others: EzLink card topup
3 0.00 10.00 2.00 20.00 32.00 Others: Caught a movie with popcorn and soda. (Teenage Mutant Minja Turtles: Out of the Shadows)
4 5.90 10.00 2.00 0.00 17.90
5 38.20 10.40 2.00 0.00 50.60 Food: Some Sunday lunch extravaganza at Ishinomaki.
6 16.65 10.70 2.00 10.00 39.35 Others: EzLink card topup
7 15.60 10.10 2.00 0.00 27.70
8 3.20 10.10 2.00 10.00 25.30 Others: EzLink card topup
9 6.00 10.10 2.00 0.00 18.10
10 0.00 10.10 2.00 38.60 50.70 Others: Contracted an eye infection and had to pay for meds.
Subtotal 102.15 101.70 20.00 88.60 312.45 Balance: 687.55

The first ten days started out promisingly. I treated myself to a movie, and over the weekend, joined some friends for a big lunch at Ishinomaki. Not really my usual fare, but I figured my budget could take it.

And then things went downhill when both my eyes started leaking copious amounts of discharge and I got a splitting headache. Finally, His Teochewness gets sick! Yay, a legit reason to use my Medical Leave entitlement! But hold on... it's a Friday evening. Ugh.

The medical fees threw my budget slightly off, but it was early in the month.

Day 11 to 20

Day Food Cigarettes SEF Others Total Comments
11 6.00 10.10 2.00 0.00 18.30
12 8.20 0.00 2.00 0.00 10.20
13 10.45 10.20 2.00 10.00 32.65 Others: EzLink card topup
14 5.90 10.10 2.00 1.60 19.60 Others: Detergent
15 5.60 10.20 2.00 10.00 27.80 Others: EzLink card topup
16 8.50 0.00 2.00 0.00 10.50
17 2.00 10.70 2.00 10.00 24.70 Others: EzLink card topup
18 3.20 10.10 2.00 0.00 15.30
19 10.30 10.70 2.00 0.00 23.00
20 3.60 10.20 2.00 0.00 15.80
Subtotal 166.00 184.10 40.00 120.20 510.30 Balance: 489.70

Nothing terribly exciting these ten days. Still recovering from the eye infection and outside of work, all strenuous activity was kept to a minimum. Less spending took place, not that I was trying particularly hard. You try getting anything done when your eyes are swollen and constantly blurring up...

Day 21 to 30

Day Food Cigarettes SEF Others Total Comments
21 7.80 10.70 2.00 10.00 30.50 Others: EzLink card topup
22 17.55 10.30 2.00 0.00 29.85
23 8.50 10.70 2.00 10.00 31.20 Others: EzLink card topup
24 0.90 10.10 2.00 0.00 13.00
25 4.60 10.10 2.00 12.50 29.20 Others: Caught a movie (Independence Day: Resurgence)
26 12.80 10.20 2.00 0.00 25.00
27 9.60 10.20 2.00 226.00 247.80 Others: Mobile bill, internet bill, utility bill, conservancy charges
28 8.50 10.20 2.00 10.00 30.70 Others: EzLink card topup
29 2.10 10.10 2.00 20.00 34.20 Others: ActiveSG monthly membership fee, Community Chest monthly donation
30 2.80 10.20 2.00 11.20 26.20 Others: EzLink card topup, tube of toothpaste
Subtotal 241.15 286.90 60.00 419.90 1007.95 Balance: -7.95

I recovered!

And just in time to catch the much-awaited sequel to the 1996 classic Independence Day.

The last ten days of the month is traditionally the most expensive, because that's when I pay all the bills, in this case, on the 27th.

On the 29th, I donated ten bucks to the Community Chest and spent another ten bucks to continue my ActiveSG monthly membership which gives me unlimited access to all Singapore Sports Council swimming complexes islandwide.

At a glance

It looks like I exceeded the budget by almost 8SGD. Gadzooks! The horror!

Next

Concluding the experiment.

Wednesday 20 July 2016

The Web Dev Budget Experiment (Part 1/3)

When I started out as a web developer back in 2008, many people questioned my career choice. They thought I should have continued being IT Support, but perhaps in a larger environment. Get more exposure. Climb the corporate ladder. Rake in the big bucks in a nice office.

They were both right and wrong.

They were right in the sense that web developers don't earn a lot unless they're working for an obscenely rich company or they're further up the pecking order, such as management. And they were wrong in the sense that web development isn't a viable career choice due to its lower earning power.

How well you do is not simply about how much you get paid each month - it's also about how you spend that money. There's no point in earning 10k per month if your lifestyle demands that you spend 12. Wealth isn't absolute - it's relative. The important thing is for expenses to be significantly less than income.
Stop being broke.

No-brainer, right?

This is common sense. Why am I even telling you this? Because an astounding number of people, in the search for the almighty dollar, forget this important point. The more they earn, the more they spend. Back to square one. Economists call it "lifestyle inflation". I call it "utter foolishness".

But now I'm curious. How much is enough? Someone told me a person needs 800SGD a month to live in Singapore. That number seemed unreasonably low. Someone else told me 2,000SGD a month was the minimum. That seemed even more ridiculous, considering the fact that plenty of people don't earn that much.

And then someone claimed it was impossible to survive on 1,000SGD a month. This got me wondering. Impossible, or just difficult? And this prompted my next experiment - how to survive on 1,000SGD a month, or less.

My Demographic

- late 30s
- unmarried, no kids
- living on my own, paying off the mortgage on a 30-year house loan.
- aging parents living nearby
- no car
- smoker
- 1,000SGD is about 30% of my monthly disposable income.

The Web Dev Budget Experiment Begins

June 2016 was the month I chose to monitor my expenses. Only my personal everyday expenses. As mentioned earlier, 1,000SGD is about 30% of my monthly disposable income. The rest covers the mortgage and paying my parents to leave me alone. Just kidding... I think.

When the financial month starts...

My payday is always on the 1st of the month. I've worked for many different companies, and they all had different paydays. For my own convenience, I maintain an account from which I pay myself every 1st day of the month. This ensures that my personal payday remains the same come hell or high water. Whatever company I'm working for at the moment, will deposit my salary into that account when it needs to.

Waypoints

For better control, I work with waypoints. On a certain day of the month, my remaining balance is supposed to be at that level. If it's not, I readjust spending.

Day 1: 1,000SGD
Day 10:  700SGD
Day 20: 400SGD
Day 25: 200SGD

The Scrooge App

To keep track of my expenses, I used an app I wrote a year back. You can find the web tutorial here.

Try my app!

Next

A day-to-day accounting.

Thursday 14 July 2016

Progress, Not Perfection

Recently, I was reminded of something called The Principle of Good Enough. Several reminders, to be exact.

This was when I got a popup notification from Microsoft asking me to upgrade to Windows 10.

Yes yes, we know.


Before that, it was in the form of an announcement that Microsoft support for Windows 8 would cease in 2018.

Shortly before that, it was in the form of a similar announcement for Windows 7.

And before that, around April 2014, yet another announcement for Windows XP.

What's all this got to do with The Principle of Good Enough, you may ask? Basically, that technology is all about progress. Progress, not perfection.

Hold on, I'm a perfectionist...

Whoa, buddy. Let me stop you right there. See, the problem I have with developers claiming to be "perfectionists" is that it's at best an extreme stance couched as a positive trait, and at worst, blatant self-praise.

Most of it misguided because, I say again, technology is all about progress, not perfection. Perfectionism is irrelevant, perhaps even poisonous, to the process.

Were any of the Windows Operating Systems "perfect"? From the ugly but serviceable Windows 3.11, to the monstrosity known as Windows Vista, to the latest iteration in the form of Windows 10? If they were "perfect", why the need for different versions? Why the need for patches?

And why, oh why, did Microsoft not wait till each version was at least bug-free before releasing to the public?

Because in technology, perfection is unprofitable.

Firstly, let's establish that there is no such thing as "perfect" unless all you're peddling is a "Hello World" kiddy script - perfect and utterly useless.

Perfectly useless.

There will always be bugs. There will always be security loopholes. But if Microsoft waited for all these to be ironed out before shipping each version, they wouldn't have sold a damn thing since 1985. Also, if you make something you consider "perfect" today, emerging technology will render it obsolete in a year.

This brings me to my second point...

Your company does not want perfection.

The duty of the developer is not to produce "perfect" software, but to produce software that will not exceed the pain threshold of their target market. Hence the Principle of Good Enough. If the user will pay for the product and not kick up too much of a stink when the inevitable bugs surface, it's "good enough". No one, not least the people paying your wages, have the time to wait for everything to be absolutely perfect before shipping.

But before this principle leads you to think you can get away with murder, think again. Just making a product shippable is a sizeable task on its own. If the product is so bug-ridden even at basic usability levels that no one will touch it with a ten-foot pole, then it is, quite simply, not good enough.

That is why, despite the Principle of Good Enough, testing is still relevant, and more important than ever.

So, perfectionists...

Remember that your first duty is to ship a viable product - whether it's an operating system, a web portal or a mobile game. Quality takes time - time that your superiors may not want to invest in. If an imperfect but serviceable system will pay the wages of your entire team for six months upon release whereas a perfect system will take another six months (while earning the company squat), which do you think upper management is likely to prefer?

Learn to look at the bigger picture. The business is far, far larger than you, your code or your principles.

Which brings to mind something I read in another blogpost, titled Don't Call Yourself A Programmer, And Other Career Advice. Now I don't always agree with McKenzie, but this hit home for me. This wasn't some airy-fairy idealism - this was real, hardcore pragmatism.
Producing beautiful software is not a goal.  Solving complex technical problems is not a goal.  Writing bug-free code is not a goal.  Using sexy programming languages is not a goal.  Add revenue.  Reduce costs.  Those are your only goals.
- Patrick McKenzie


Errata

In a previous blogpost, I provided a very shallow look at the difference between programmers and web developers.

Programmers often criticize web developers for not being thorough enough and being too willing to take shortcuts in order to get the job done, and lack of long-term vision. That's not wrong, but look at the context. Software is made to last, therefore programmers are allowed - encouraged, even - to do a proper, complete job.


Obviously, now that I consider this new evidence, I was mistaken.

Ah well, nobody's perfect.
T___T

Tuesday 5 July 2016

Web Tutorial: QBasic Bubble Sort Animation

Sorting data is one of the cornerstones of information technology. And the numero uno method, not in terms of ingenuity but in terms of sheer simplicity, is the Bubble Sort. How it works is simple.

Traverse a list of numbers, like the one below.
93,12,777,90

Each time you encounter a number, compare it with the number after that. If the first number is larger than the second number, swap their positions in the list. The numbers swapped are in red for clarity.

93,12,777,90
12,93,777,90

Carry on till you reach the end of the list, then begin anew at the beginning of the list. Do this till you have traversed the entire list, from first to last number, with no swaps taking place.

12,93,777,90
12,93,90,777
12,93,90,777
12,90,93,777
12,90,93,777

And thus, the list is sorted. Now, just for the heck of it, let's do this in QBasic! Here, we have a variable called sortlistsize which defines the number of numbers you wish to sort. This will be input by the user.
PRINT "T___T's BUBBLE SORT"
INPUT "What is the size of your list"; sortlistsize

After that, we use the number to define the size of the array sortlist, then use a For loop to ask the user to input all those numbers.
PRINT "T___T's BUBBLE SORT"
INPUT "What is the size of your list";

DIM sortlist(sortlistsize) 

FOR i = 0 TO (sortlistsize - 1) STEP 1 
    INPUT "Please enter Number: ", sortlist(i) 
NEXT i

If you run the code now, this is what you'll get.
 

After this, we define the variables col and row.
DIM sortlist(sortlistsize)

 FOR i = 0 TO (sortlistsize - 1) STEP 1
    INPUT "Please enter Number: ", sortlist(i)
NEXT i

DIM col, row 

col = 2 
row = 2

We're going to define four colors next - for the current numbers being sorted, and those not being sorted. For each category, we have a color for foreground and background. To find out what the values mean, look up the QBasic color chart.
DIM col, row

col = 2
row = 2

LOCATE row, col

DIM current_fc, current_bc
current_fc = 15
current_bc = 4

DIM unsorted_fc, unsorted_bc
unsorted_fc = 0
unsorted_bc = 14

Next, we define more variables. sorted is either 0 or 1, and is 1 only when the entire list has been sorted.  
passes keeps track of the number of passes so far.  
swaps keeps track of the number of swaps during the current pass. This value is reset in the next pass. currentpos keeps track of the cursor's current position in the sortlist array.
temp is a variable for holding the current value of the number to be swapped.
totalswaps, just for fun, shows us how many swaps it took for your list to get sorted.

DIM current_fc, current_bc
current_fc = 15
current_bc = 4

DIM unsorted_fc, unsorted_bc
unsorted_fc = 0
unsorted_bc = 14

DIM sorted, passes, swaps, currentpos, temp, totalswaps
sorted = 0
passes = 0
swaps = 0
totalswaps = 0
currentpos = 0

Here, we set the color to black and clear the screen using that color.
DIM sorted, passes, swaps, currentpos, temp, totalswaps
sorted = 0
passes = 0
swaps = 0
totalswaps = 0
currentpos = 0

COLOR 0, 0
CLS

This next While loop effectively means that the program continues execution till sorted becomes 1, which in turn means that the sortlist array has been completely sorted. Do not run your code at this time - you're only going to get an infinite loop because there's no exit clause.
COLOR 0, 0
CLS

WHILE sorted = 0

WEND

This introduces a slight pause that begins with every new pass. The cursor is then moved to the position on screen specified by row and col, which is, at the start of the loop, near the top left.
WHILE sorted = 0
    SLEEP 2
    LOCATE row, col

WEND

We're going to draw the entire series of numbers in the sortlist array on screen, in the appropriate colors. To do this, we use a For loop.
WHILE sorted = 0
    SLEEP 2
    LOCATE row, col

    FOR i = 0 TO (sortlistsize - 1) STEP 1

    NEXT i

WEND

Now, this next conditional checks if the variable i is at the current position of the cursor, or the next one. If so, it uses the current colors; otherwise it uses the default colors. The value is then drawn out on screen.
WHILE sorted = 0
    SLEEP 2
    LOCATE row, col

    FOR i = 0 TO (sortlistsize - 1) STEP 1
        IF i = currentpos OR currentpos + 1 = i THEN
            COLOR current_fc, current_bc
        ELSE
            COLOR unsorted_fc, unsorted_bc
        END IF

        PRINT sortlist(i);

    NEXT i
WEND

The color is changed back to black, and a space is printed as a separator.
WHILE sorted = 0
    SLEEP 2
    LOCATE row, col

    FOR i = 0 TO (sortlistsize - 1) STEP 1
        IF i = currentpos OR currentpos + 1 = i THEN
            COLOR current_fc, current_bc
        ELSE
            COLOR unsorted_fc, unsorted_bc
        END IF

        PRINT sortlist(i);
        COLOR 0, 0
        PRINT " ";

    NEXT i
WEND

After the entire list of numbers has been drawn on screen, we start sorting. Here, we provide an If conditional. If the number represented at the current position of the list represented by the variable currentpos is greater than the next...
    FOR i = 0 TO (sortlistsize - 1) STEP 1
        IF i = currentpos OR currentpos + 1 = i THEN
            COLOR current_fc, current_bc
        ELSE
            COLOR unsorted_fc, unsorted_bc
        END IF

        PRINT sortlist(i);
        COLOR 0, 0
        PRINT " ";
    NEXT i

    IF sortlist(currentpos) > sortlist(currentpos + 1) THEN

    END IF

WEND

...we swap their places with the use of the variable temp.
    IF sortlist(currentpos) > sortlist(currentpos + 1) THEN
        temp = sortlist(currentpos)
        sortlist(currentpos) = sortlist(currentpos + 1)
        sortlist(currentpos + 1) = temp

    END IF
WEND

We then increment the variable swaps, and tell the processor to pause for 1 second, just for effect.
    IF sortlist(currentpos) > sortlist(currentpos + 1) THEN
        temp = sortlist(currentpos)
        sortlist(currentpos) = sortlist(currentpos + 1)
        sortlist(currentpos + 1) = temp

        swaps = swaps + 1

        SLEEP 1

    END IF
WEND

We then go to the next line using the LOCATE command, and print out some information regarding the swap.
    IF sortlist(currentpos) > sortlist(currentpos + 1) THEN
        temp = sortlist(currentpos)
        sortlist(currentpos) = sortlist(currentpos + 1)
        sortlist(currentpos + 1) = temp

        swaps = swaps + 1

        SLEEP 1

        LOCATE row + 1, col
        COLOR 14, 0
        PRINT "Swapping ";
        PRINT temp;
        PRINT " with ";
        PRINT sortlist(currentpos);
        PRINT "...";

    END IF
WEND

And if no sort takes place, we clear the screen.
    IF sortlist(currentpos) > sortlist(currentpos + 1) THEN
        temp = sortlist(currentpos)
        sortlist(currentpos) = sortlist(currentpos + 1)
        sortlist(currentpos + 1) = temp

        swaps = swaps + 1

        SLEEP 1

        LOCATE row + 1, col
        COLOR 14, 0
        PRINT "Swapping ";
        PRINT temp;
        PRINT " with ";
        PRINT sortlist(currentpos);
        PRINT "...";
    ELSE
        CLS

    END IF
WEND

After that, we repeat the code block which was drawing the list of numbers. Yes I know, good programming practices dictate that I should write this as a function and avoid repeating myself. And I should, for the purposes of code efficiency, check if there are any swaps before redrawing. But honestly? This is pretty much throwaway code, so to hell with that.
    FOR i = 0 TO (sortlistsize - 1) STEP 1
        IF i = currentpos OR currentpos + 1 = i THEN
            COLOR current_fc, current_bc
        ELSE
            COLOR unsorted_fc, unsorted_bc
        END IF

        PRINT sortlist(i);
        COLOR 0, 0
        PRINT " ";
    NEXT i

    IF sortlist(currentpos) > sortlist(currentpos + 1) THEN
        temp = sortlist(currentpos)
        sortlist(currentpos) = sortlist(currentpos + 1)
        sortlist(currentpos + 1) = temp

        swaps = swaps + 1

        SLEEP 1

        LOCATE row + 1, col
        COLOR 14, 0
        PRINT "Swapping ";
        PRINT temp;
        PRINT " with ";
        PRINT sortlist(currentpos);
        PRINT "...";
    ELSE
    CLS
    END IF

    FOR i = 0 TO (sortlistsize - 1) STEP 1
        IF i = currentpos OR currentpos + 1 = i THEN
            COLOR current_fc, current_bc
        ELSE
            COLOR unsorted_fc, unsorted_bc
        END IF

        PRINT sortlist(i);
        COLOR 0, 0
        PRINT " ";
    NEXT i

WEND

The next block of code moves two lines down and prints stats.
    FOR i = 0 TO (sortlistsize - 1) STEP 1
        IF i = currentpos OR currentpos + 1 = i THEN
            COLOR current_fc, current_bc
        ELSE
            COLOR unsorted_fc, unsorted_bc
        END IF

        PRINT sortlist(i);
        COLOR 0, 0
        PRINT " ";
    NEXT i

    IF sortlist(currentpos) > sortlist(currentpos + 1) THEN
        temp = sortlist(currentpos)
        sortlist(currentpos) = sortlist(currentpos + 1)
        sortlist(currentpos + 1) = temp

        swaps = swaps + 1

        SLEEP 1

        LOCATE row + 1, col
        COLOR 14, 0
        PRINT "Swapping ";
        PRINT temp;
        PRINT " with ";
        PRINT sortlist(currentpos);
        PRINT "...";
    ELSE
        CLS
    END IF

    FOR i = 0 TO (sortlistsize - 1) STEP 1
        IF i = currentpos OR currentpos + 1 = i THEN
            COLOR current_fc, current_bc
        ELSE
            COLOR unsorted_fc, unsorted_bc
        END IF

        PRINT sortlist(i);
        COLOR 0, 0
        PRINT " ";
    NEXT i

    LOCATE row + 2, col
    COLOR 14, 0
    PRINT "Passes: ";
    PRINT passes;
    PRINT "Swaps: ";
    PRINT swaps;

WEND

And after printing out the stats, we increment the variable currentpos and then check if it has reached the end of the list. If so, we start tracking stats. The variable passes is incremented because getting to the end of the list counts as one pass completed. If no swaps have taken place during the entire pass, this means the list is sorted and the variable sorted can be set to 1. Otherwise, the number of swaps in this current pass is added to the running total.
    LOCATE row + 2, col
    COLOR 14, 0
    PRINT "Passes: ";
    PRINT passes;
    PRINT "Swaps: ";
    PRINT swaps;

    currentpos = currentpos + 1

    IF currentpos = sortlistsize - 1 THEN
        currentpos = 0
        passes = passes + 1

        IF swaps = 0 THEN
            sorted = 1
        ELSE
            totalswaps = totalswaps + swaps
        END IF

        swaps = 0
    END IF

WEND

So now we finally have a condition where the While loop can be exited. The next segment of code clears the screen in black and prints out the entire sort list, sans current marker.
WEND

COLOR 0, 0
CLS
LOCATE row, col

FOR i = 0 TO (sortlistsize - 1) STEP 1
    COLOR unsorted_fc, unsorted_bc
    PRINT sortlist(i);
    COLOR 0, 0
    PRINT " ";
NEXT i

And then we print out more stats to show how many passes and swaps it took to sort the list.
FOR i = 0 TO (sortlistsize - 1) STEP 1
    COLOR unsorted_fc, unsorted_bc
    PRINT sortlist(i);
    COLOR 0, 0
    PRINT " ";
NEXT i

LOCATE row + 2, col
COLOR 14, 0
PRINT "SORTED WITH PASSES: ";
PRINT passes;
LOCATE row + 3, col
PRINT "TOTAL SWAPS: ";
PRINT totalswaps; s

Enjoy your sorting! Run your code. Enter in the size of your list followed by the numbers, then watch the magic unfold.




Note! 

List sizes of 1 and 0 are automatically considered sorted. However, this has not been handled in the code, so if you try it, you're likely to break something. I just want to point out that this isn't a great practice as far as programming goes. I figure we can get away with it today because we're just messing about with QBasic and a Bubble Sort. Very trivial stuff.

Thanks, guys. I had a great time. Sort of.
T___T