Sunday, 27 May 2018

Rise of the Bike-sharing Phenomenon

With mobile technology now invading just about every aspect of our lives, it should come as little surprise that bike-sharing, which has been a thing since the 60s, is now available via apps. Dockless bicycle sharing has been in Singapore since last year with moderate success. Today, no less than six different companies vie for their share of the consumer pie.



Basically, you download the appropriate app on your mobile phone, tie your credit card account to it, and use the scanner function in the app (Bluetooth required) to unlock the bike.

The timer starts, and does not stop until you park and lock the bike.

Here's the low-down on the bike-sharing phenomenon...


Conveniently available.

The Good

Generally cheap. Upon signing up, most bike-sharing operators will give you some sort of special trial period offer. Competition is intense on this tiny island.

Perks for responsible use. Parking the bicycle in an approved spot or reporting damaged bikes nets you credit. This credit may offset the cost of your next bike-share.

Convenience. Being able to get from Point A to Point B is a perk that just cannot be understated. If I fall asleep on the bus and miss my stop, having a bicycle at my disposal will ensure I still make it to the office on time. Being able to use a bicycle without the hassle of actual ownership, is great.

Mobile-enabled. Having these services available via mobile is a huge step in the right direction. Yes, I'm aware this started way before last year, but still...


Anyone feel like riding
on this rustbucket?

The Bad

Uneven distribution. If you only use one bike-share app, finding an appropriate bike for your use from that one operator, is a bit of a crapshoot. Certain bikes from certain vendors are simply more plentiful at certain places.

Damaged bikes. There have been times when I found a bike, and the handlebars came off in my hand. Or those whose brakes have been shot to hell. Or those that have been out in the rain so long, the frame has corroded and riding on them is pretty much taking your life in your grubby little hands.

Finding a good fit. The bikes cater to the average build. If you're exceptionally fat, tall or lightweight, there just aren't that many bikes you can simply pick up and ride without a certain amount of discomfort.

Language. Some of the apps are almost incomprehensible. Ofo, for instance, comes from China and the English they use in their app... horrible does not begin to describe it.


Randomly dumped.

The Downright Ugly


Bike-hogging. I've seen these bikes being parked outside someone's front door, as if they own them. Worse - I've actually seen personal bicycle locks on the wheels of these bikes.

Dumping. Some people just toss the bikes when they're done. In the drain, in the grass, in the middle of the road... seriously, guys?

Vandalism. I can understand wear and tear. I can understand a few knocks here and there. What I can't condone is the seat of a bike being knifed, or your initials being scraped into the body. Or pedals missing. That's just being a douche.

Operator names. Can I just say that the names of the operators are really terrible? Firstly, they have very similar names to each other. And then there's Ofo, which rhymes with the short-form for an impolite term describing an incestuous dude. And then there's GBikes. When you say it fast, it sounds like a Hokkien expletive. The only possible exception is Baicycle, which is a pun on the Chinese word for "white". And damn, I love puns. The lamer, the better.

The Future

Bike-sharing's here to stay, that's for sure. However, with the rising costs of a bike-share, along with up-front payment implemented by many vendors, casual users are being put off, while serious users will probably buy their own. Kind of like the rising costs of ride-sharing - it was a good thing while it lasted.

Singapore's a tiny island, and over time it's my belief that one or two operators will trump the rest. There's just not enough space for that many bike-sharing operators here. Something has to give, eventually.


You don't GBike!
T___T 

Wednesday, 23 May 2018

The WSQ Way to Self-improvement

Identifying and addressing knowledge gaps is part and parcel of being a web developer; indeed just about any technical field. I remarked on this last month, and now here's a concrete suggestion. Today, I'd like to cover one of the ways in which developers may find out exactly what knowledge they lack.

In a field as wide and varied as software development, there is a very real danger of specializing too much, or worse; not realizing where the knowledge gaps are in the first place, and what's required by the industry. Because the answer to that question varies depending on who you ask. In cases like these, we need a singularly reliable source of truth - the Government. I'm not saying that I agree with everything the Government pushes, but certification from the Government does mean that if you have such a certification and seek employment in Singapore, there are very few organizations that can argue against your certification. After all, this spec was developed by industry experts and endorsed by the highest authority in the land.

Cover

The Workforce Skills Qualification credential system was set up by the Singapore Government to set up competency frameworks across a variety of industries, so as to improve labor movement and quality control. A few of these frameworks are pertinent to web development - InfoComm and Creative Industries. For today's example, I downloaded a copy of a Competency Unit - Code Scripts to Provide Front-End Functionality for Websites - from the Creative Industries WSQ framework at https://www.skillsconnect.gov.sg/sop/portal/. By referring to this document, if you're a front-end developer, even (or especially) if you're an aspiring one, you may be able to identify areas in which you can shore up your skills and knowledge to meet industry standards.

Here's a breakdown of the spec...

Relevant Job Roles/Occupations
Assumed Skills and Knowledge
Performance Statements

Relevant Job Roles/Occupations

This basically lets you know what roles or occupations would find this relevant. Pretty straightforward there... though I can't say I agree with the term "HTML Programmer". What is this, 1995?!

Assumed Skills and Knowledge

This is a list of ideal prior knowledge for taking up training in this Competency Unit. Meaning, it'll be a whole lot easier for you to learn this Competency Unit if you know this stuff. I'm not sure that these should be optional, though. I mean, if you don't know HTML, CSS or even basic programming, what the hell are you doing trying to code scripts? Most, if not all, scripts manipulate the Document Object Model, which is the product of HTML. Honestly, having no knowledge of HTML while trying to learn scripting, is the software equivalent of attempting the hundred meter dash without learning to walk first.

Performance Statements

A list of items, in no particular order of importance, that someone should be able to demonstrate in order to pass this Competency Unit.

Pay attention to this one. Be honest with yourself as you assess your ability to perform each of these tasks. Anything you're not so great at? Anything you've never heard of at all? These are excellent starting points.


Underpinning Knowledge

Underpinning Knowledge

This is all the knowledge that should be behind the items you have performed above. There is, for example, no sense in writing a HTML page and claiming you know how to write a HTML page when you don't even know what "HTML" stands for. Or being able to write scripts that do stuff, without knowing what the lines in the script do and why they are required.

We've all dealt with those devs before, from time to time we may even be them - they're called "copy-paste programmers".

As in the Performance Statements, what's needed is for you to go through this list and see if there's anything you don't know, then start rectifying your shortfalls from there.

Range of Application

Range of Application

This is about contextualization. Not every tech job requires you to know everything under the Performance Statements and Underpinning Knowledge. For example, not every tech context requires objects... or even loops (though that would be pretty strange). So there are some examples of each Performance Statement and Underpinning Knowledge, of which a subset has to be fulfilled.

Research your competencies!

A tech career is an ongoing progress. The day you stop learning, the day you stop wanting to learn, your career is already dead - you just don't know it yet.

Use the (work)Force, Luke!
T___T


Friday, 18 May 2018

Web Tutorial: The Tower of Hanoi (Part 3/3)

The final part of this web tutorial is here, and it's perhaps the most important thing we'll be doing.

Comment out this part. Or erase it.
'openpiece = pop(1)
'push 2, openpiece
'openpiece = pop(1)


Declare the variable operation. It's not "shared" because it isn't referenced in any subroutine or function.
DIM SHARED stack(3, 4) AS INTEGER
DIM SHARED top(3) AS INTEGER
DIM SHARED piece(4) AS STRING
DIM SHARED openpiece AS INTEGER
DIM operation AS INTEGER


Now, let's wrap the call to drawstack() in a While loop. As long as the third rod does not have four disks, the user has not won the game.
'openpiece = pop(1)
'push 2, openpiece
'openpiece = pop(1)

WHILE top(3) < 4
    drawstack
WEND


Now, set operation to 0. And create a While loop within the current While loop, that will run for as long as operation is 0.
WHILE top(3) < 4
    drawstack

    operation = 0

    WHILE operation = 0

    WEND
WEND


Move the cursor to below the drawn setup, and proceed to ask for input from the user. If openpiece is 0, that means all disks are on rods. So you have to ask which piece to remove. If it's not 0, then that means there is an open, unassigned piece and you need to place it on a rod. Whatever the user input it, assign the value to the variable selected.
WHILE top(3) < 4
    drawstack

    operation = 0

    WHILE operation = 0
        LOCATE 12, 2
        IF openpiece = 0 THEN
            INPUT "Remove piece  "; selected
        ELSE
            INPUT "Place onto rod"; selected
        END IF
    WEND
WEND


Next, set the value of operation to the value returned by the function doable(), passing in selected as an argument.
WHILE top(3) < 4
    drawstack

    operation = 0

    WHILE operation = 0
        LOCATE 12, 2
        IF openpiece = 0 THEN
            INPUT "Remove piece    "; selected
        ELSE
            INPUT "Place onto stack"; selected
        END IF

        operation = doable(selected)
    WEND
WEND


And then let's create the doable() function. First, set the function to return 0 by default.
SUB drawstack ()
FOR i = 1 TO 6
    LOCATE i + 1, 2

    FOR j = 1 TO 3
        IF i = 1 THEN
            drawpiece 0
        ELSE
            IF i = 6 THEN
                drawbase j
            ELSE
                drawpiece (stack(j, 6 - i))
            END IF
        END IF
    NEXT j
NEXT i

COLOR 15, 0
LOCATE 9, 2
PRINT "Open ";
IF openpiece = 0 THEN
    PRINT "(none)         ";
ELSE
    drawpiece openpiece
END IF
END SUB

FUNCTION doable (userinput)
doable = 0
END FUNCTION


Again, we set  a conditional revolving around whether openpiece is 0 or not.
FUNCTION doable (userinput)
doable = 0

IF openpiece = 0 THEN

ELSE

END IF
END FUNCTION


So if there is no disk currently unassigned to any rod, iterate from the first rod to the third. And if the appropriate element of the top array is greater than zero (which means that particular rod is not empty)...
FUNCTION doable (userinput)
doable = 0
IF openpiece = 0 THEN
    FOR i = 1 TO 3
        IF top(i) > 0 THEN

        END IF
    NEXT
ELSE

END IF
END FUNCTION


...and if the number of the topmost disk on that rod matches userinput, then set doable to 1 and openpiece to the value returned by pop(), with the number of that rod passed in as an argument. It's not the most efficient way ever, but it works. This also means that if the user enters in some funny number, it won't work.
FUNCTION doable (userinput)
doable = 0
IF openpiece = 0 THEN
    FOR i = 1 TO 3
        IF top(i) > 0 THEN
            IF stack(i, top(i)) = userinput THEN
                doable = 1
                openpiece = pop(i)
            END IF
        END IF
    NEXT
ELSE

END IF
END FUNCTION


Now, if openpiece isn't 0, that means you need to "push" the disk on the indicated rod. First, we need to ascertain that userinput is between 1 and 3. Seems a bit defensive, but it's necessary.
FUNCTION doable (userinput)
doable = 0
IF openpiece = 0 THEN
    FOR i = 1 TO 3
        IF top(i) > 0 THEN
            IF stack(i, top(i)) = userinput THEN
                doable = 1
                openpiece = pop(i)
            END IF
        END IF
    NEXT
ELSE
    IF userinput >= 1 AND userinput <= 3 THEN

    END IF
END IF
END FUNCTION


Now, you can place the disk on the rod only if the topmost disk on the rod is larger than the disk you're trying to "push", or if that rod is empty anyway.
FUNCTION doable (userinput)
doable = 0
IF openpiece = 0 THEN
    FOR i = 1 TO 3
        IF top(i) > 0 THEN
            IF stack(i, top(i)) = userinput THEN
                doable = 1
                openpiece = pop(i)
            END IF
        END IF
    NEXT
ELSE
    IF userinput >= 1 AND userinput <= 3 THEN
        IF stack(userinput, top(userinput)) > openpiece OR top(userinput) = 0 THEN

        END IF
    END IF
END IF
END FUNCTION


Set doable to 1 and execute the appropriate push().
FUNCTION doable (userinput)
doable = 0
IF openpiece = 0 THEN
    FOR i = 1 TO 3
        IF top(i) > 0 THEN
            IF stack(i, top(i)) = userinput THEN
                doable = 1
                openpiece = pop(i)
            END IF
        END IF
    NEXT
ELSE
    IF userinput >= 1 AND userinput <= 3 THEN
        IF stack(userinput, top(userinput)) > openpiece OR top(userinput) = 0 THEN
            doable = 1
            push userinput, openpiece
        END IF
    END IF
END IF
END FUNCTION


Now start the program and try to place the rods!









Some improvements

We need to keep track of the number of moves! So let's create a variable, moves. Set it to 0.
DIM SHARED stack(3, 4) AS INTEGER
DIM SHARED top(3) AS INTEGER
DIM SHARED piece(4) AS STRING
DIM SHARED openpiece AS INTEGER
DIM operation AS INTEGER
DIM SHARED moves AS INTEGER

piece(1) = " 1 "
piece(2) = "  2  "
piece(3) = "   3   "
piece(4) = "    4    "
openpiece = 0

FOR i = 1 TO 3
    FOR j = 1 TO 4
        stack(i, j) = 0
    NEXT j

    top(i) = 0
NEXT i

push 1, 4
push 1, 3
push 1, 2
push 1, 1
moves = 0


In the push() subroutine, increment moves at the end. This means that every "push" is counted as one move. Yes, that means after all the initial calls to push(), openpiece should be 4. Now see why you set moves to 0 after the push() calls?
SUB push (stackno, value)
top(stackno) = top(stackno) + 1
stack(stackno, top(stackno)) = value
moves = moves + 1
openpiece = 0
END SUB


Now, at the end of the drawstack() subroutine, after printing out openpiece, print out the moves used so far!
SUB drawstack ()
FOR i = 1 TO 6
    LOCATE i + 1, 2

    FOR j = 1 TO 3
        IF i = 1 THEN
            drawpiece 0
        ELSE
            IF i = 6 THEN
                drawbase j
            ELSE
                drawpiece (stack(j, 6 - i))
            END IF
        END IF
    NEXT j
NEXT i

COLOR 15, 0
LOCATE 9, 2
PRINT "Open ";
IF openpiece = 0 THEN
    PRINT "(none)         ";
ELSE
    drawpiece openpiece
END IF

LOCATE 10, 2
PRINT "Moves ";
PRINT moves;
END SUB


Go ahead, run the program again. Does the game keep track of the number of moves you have made?








Now, for one more improvement. Tell the user when they have won the game. After that While loop, call drawstack() again and move the cursor, then print the congratulatory message. This is logical because they can only have won if they've exited the While loop, meaning that top(3) is 4, which in turn means that all four disks are on the third rod.

WHILE top(3) < 4
    drawstack

    operation = 0

    WHILE operation = 0
        LOCATE 12, 2
        IF openpiece = 0 THEN
            INPUT "Remove piece  "; selected
        ELSE
            INPUT "Place onto rod"; selected
        END IF

        operation = doable(selected)
    WEND
WEND

drawstack
LOCATE 12, 2
PRINT "Congratulations! You have solved the Tower of Hanoi!"


Try it! I used 26 moves to solve the game. You can (and should) do a lot better.


Notes

This code hasn't been fully abstracted, which is geek-speak to say, if you want to modify the code to incorporate more disks or change the background color and such, there are a few places you have to change. Still, this is just an exercise in programming logic. Don't worry too much about it. I certainly didn't.

Sure, take the code and make it better. That's what sharing is for.

Many towering thanks,
T___T

Wednesday, 16 May 2018

Web Tutorial: The Tower of Hanoi (Part 2/3)

Hello, and welcome back. This part will be a little bit technical, but duh, it's a programming tutorial.

Let's declare another array, stack. This will be a two-dimensional array. It represents your Tower of Hanoi setup - there are three bases and rods, and each one has four slots. Four slots because we have four disks, right? Then create another array, top. Each element represents the current height of the stacked disks on each rod.

DIM SHARED stack(3, 4) AS INTEGER
DIM SHARED top(3) AS INTEGER 
DIM SHARED piece(4) AS STRING


Now, let's initialize the stack array. Use a nested For loop to ensure that every slot in each base and rod, is set to 0. And make sure that all elements of the top array are set to 0.

DIM SHARED stack(3, 4) AS INTEGER
DIM SHARED top(3) AS INTEGER
DIM SHARED piece(4) AS STRING

piece(1) = " 1 "
piece(2) = "  2  "
piece(3) = "   3   "
piece(4) = "    4    "

FOR i = 1 TO 3
    FOR j = 1 TO 4
        stack(i, j) = 0
    NEXT j

    top(i) = 0
NEXT i

drawstack

SUB drawpiece (pieceno)
COLOR 0, 0
FOR i = 1 TO (5 - pieceno)
    PRINT " ";
NEXT i


Up to now, we've been using the word "stack" to mean "base and rod", but today we'll go a bit further and implement a data structure stack. For that to happen, we must have a pop() function and a push() subroutine. First, let's create with the push() subroutine. It will accept two parameters - stackno, and value.

DIM SHARED stack(3, 4) AS INTEGER
DIM SHARED top(3) AS INTEGER
DIM SHARED piece(4) AS STRING

piece(1) = " 1 "
piece(2) = "  2  "
piece(3) = "   3   "
piece(4) = "    4    "

FOR i = 1 TO 3
    FOR j = 1 TO 4
        stack(i, j) = 0
    NEXT j

    top(i) = 0
NEXT i

drawstack

SUB push (stackno, value)

END SUB

SUB drawpiece (pieceno)
COLOR 0, 0
FOR i = 1 TO (5 - pieceno)
    PRINT " ";
NEXT i


stackno is the base and rod you are currently referencing. value is the number of the disk you are "pushing". Remember that in a stack, it's Last In First Out. When you "push", you are stacking a disk onto a rod, on top of any other disks that may be already there. When you "pop", you remove the disk at the top, that is, the most recently "pushed" disk.

So first, increment the element of the top array referenced by stackno.

SUB push (stackno, value)
top(stackno) = top(stackno) + 1
END SUB


Then set the element of stack, referenced by stackno and the current value of the element of top you just incremented, to value. In layman's terms, that means you set the current topmost slot of the base and rod of stackno, to the disk you are "pushing".

SUB push (stackno, value)
top(stackno) = top(stackno) + 1
stack(stackno, top(stackno)) = value
END SUB


Now, in your drawstack() subroutine, alter this line to draw the pieces in that stack instead of just the rod.

SUB drawstack ()
FOR i = 1 TO 6
    LOCATE i + 1, 2

    FOR j = 1 TO 3
        IF i = 1 THEN
            drawpiece 0
        ELSE
            IF i = 6 THEN
                drawbase j
            ELSE
                drawpiece (stack(j, 6 - i))
            END IF
        END IF
    NEXT j
NEXT i
END SUB


Now try this. "Push" Disk 4 onto Stack 1, starting with the largest stack, just before the calling drawstack() subroutine.

push 1, 4

drawstack

SUB push (stackno, value)
top(stackno) = top(stackno) + 1
stack(stackno, top(stackno)) = value
END SUB


You just put the largest disk onto the first rod!



Now try this...

push 1, 4
push 1, 3

drawstack

SUB push (stackno, value)
top(stackno) = top(stackno) + 1
stack(stackno, top(stackno)) = value
END SUB


Here, you've stacked the second largest disk onto the first rod, right on top of the largest disk.



push 1, 4
push 1, 3
push 1, 2
push 1, 1

drawstack

SUB push (stackno, value)
top(stackno) = top(stackno) + 1
stack(stackno, top(stackno)) = value
END SUB


And so on. This, coincidentally, is the starting configuration of the Tower of Hanoi.



Now, let's do the "pop". For that, we'll need to declare another variable, openpiece. It holds the number of the disk that is currently not assigned to any rod. At any time, there can be only one of these.  Set the value to 0.

DIM SHARED stack(3, 4) AS INTEGER
DIM SHARED top(3) AS INTEGER
DIM SHARED piece(4) AS STRING
DIM SHARED openpiece AS INTEGER

piece(1) = " 1 "
piece(2) = "  2  "
piece(3) = "   3   "
piece(4) = "    4    "
openpiece = 0


In the push() subroutine, set openpiece to 0 after pushing. This indicates that no disks are unassigned to rods. It's not important now, but later it will be.

Now, let's create a function, pop(). It will accept a parameter stackno, which is the number of the rod you're trying to remove a disk from.

SUB push (stackno, value)
top(stackno) = top(stackno) + 1
stack(stackno, top(stackno)) = value
openpiece = 0
END SUB

FUNCTION pop (stackno)

END FUNCTION

Hold on... why is push() a subroutine and pop() a function?

Because when you pop a stack, you want to know the number of the disk you removed. Therefore the function will return that number. With push(), you don't need to return anything.

So yeah, first you assign the returned value to the number of the most recently "pushed" disk onto the rod (the stack array) indicated by stackno.

FUNCTION pop (stackno)
pop = stack(stackno, top(stackno))
END FUNCTION


Then you assign that slot in the stack array to 0, since that slot is no longer occupied. And decrement the appropriate element in the top array to say that there is one less disk in that rod now.

FUNCTION pop (stackno)
pop = stack(stackno, top(stackno))
stack(stackno, top(stackno)) = 0
top(stackno) = top(stackno) - 1
END FUNCTION


Let's modify the drawstack() subroutine to display the piece that is currently open. First, reset the color to white on black, and then move the cursor to below the drawn stack. And print the word "Open".

SUB drawstack ()
FOR i = 1 TO 6
    LOCATE i + 1, 2

    FOR j = 1 TO 3
        IF i = 1 THEN
            drawpiece 0
        ELSE
            IF i = 6 THEN
                drawbase j
            ELSE
                drawpiece (stack(j, 6 - i))
            END IF
        END IF
    NEXT j
NEXT i

COLOR 15, 0
LOCATE 9, 2
PRINT "Open ";
END SUB


Now, if the value of openpiece is 0, print something to indicate so. Otherwise, invoke the drawpiece() subroutine, passing in the value of openpiece as an argument.

COLOR 15, 0
LOCATE 9, 2
PRINT "Open ";

IF openpiece = 0 THEN
    PRINT "(none)         ";
ELSE
    drawpiece openpiece
END IF


Now, try this. After pushing all four disks onto the first rod, "pop" the first rod. And then call the drawstack() subroutine.

push 1, 4
push 1, 3
push 1, 2
push 1, 1
openpiece = pop(1)

drawstack


You just removed Disk 1 (the blue and smallest one) from the first rod.



Now try this... it pushes the unassigned disk (Disk 1) to the second rod, then removes the next disk from the first rod.

push 1, 4
push 1, 3
push 1, 2
push 1, 1
openpiece = pop(1)
push 2, openpiece
openpiece = pop(1)

drawstack


You're getting the hang of this...



Next

Game logic will tie everything up nicely, now that you've gotten the drawing subroutines and "pop" and "push" functionality out of the way.

Sunday, 13 May 2018

Web Tutorial: The Tower of Hanoi (Part 1/3)

It's time for more QBasic! It'll be something fun today. (Isn't it always?)

The Tower of Hanoi is a puzzle game where you have to move all the disks from one rod to another, stacked in descending order of size (largest at the bottom, smallest on top) using as few moves as possible. Taking out one disk from a rod and placing it on another rod counts as one move. There are, in the basic configuration, three rods and four disks. Additionally,  larger disks cannot be stacked on top of smaller disks.

For the first part of this tutorial, let's focus on drawing the rods and disks. Honestly, that's the easy part. First, let's assume that the background is black. If you really want, you can use a variable for that, but I'm just going to go with black.

Let's start with drawing the bases. The bases will be a nice brown color. That's 6 in QBasic. Here, we implement the action as a subroutine drawbase(), that accepts a parameter stackno. We'll put in a call to drawbase(), adding an argument of "1", to test.
drawbase 1

SUB drawbase (stackno)

END SUB


Set the foreground to black and the background to brown. Then print three brown spaces, followed by stackno, followed by another three brown spaces. Reset the colors at the end of the subroutine.
SUB drawbase (stackno)
COLOR 0, 6
FOR i = 1 TO 3
    PRINT " ";
NEXT

PRINT stackno;

FOR i = 1 TO 3
    PRINT " ";
NEXT

COLOR 15, 0


END SUB


This is what you should have. Simple enough, right?

EDITOR'S NOTE: The following screenshot is incorrect because I neglected to reset the colors originally. You should really be seeing "Press any key to continue" in white text on black background.


Now, delete the call to drawbase(), create a call to drawpiece() instead (using the same argument of "1") and create another subroutine, drawpiece(). This will accept the parameter pieceno.
drawpiece 1

SUB drawpiece (pieceno)

END SUB

SUB drawbase (stackno)
COLOR 0, 6
FOR i = 1 TO 3
    PRINT " ";
NEXT

PRINT stackno;

FOR i = 1 TO 3
    PRINT " ";
NEXT

COLOR 15, 0
END SUB


Also, define the array piece. Then initialize the values. Each element in the piece array represents a disk. See how the smallest one is 1, followed by 2, and so on? The number of spaces also corresponds to the number of the disk. For instance, the first element of piece, piece(1), has one space on either side of the "1". For piece(2), there are two spaces on either side of the "2". This can be done programmatically, but I don't see the point since we're not reusing this anywhere.

DIM SHARED piece(4) AS STRING

piece(1) = " 1 "
piece(2) = "  2  "
piece(3) = "   3   "
piece(4) = "    4    "

drawpiece 1

SUB drawpiece (pieceno)

END SUB


Now drawpiece() will print black spaces in front and back, just like in drawbase(). However, unlike drawbase(), the size of the piece varies according to the number. The bigger the number, the bigger the piece, and therefore the smaller the black spaces. The largest piece is piece(4), so piece(4) will have  (5 - 4 = 1) black space preceding and after it. The smallest piece, piece(1), will have (5 - 1 = 4) black spaces.
SUB drawpiece (pieceno)
COLOR 0, 0
FOR i = 1 TO (5 - pieceno)
    PRINT " ";
NEXT i

COLOR 0, 0
FOR i = 1 TO (5 - pieceno)
    PRINT " ";
NEXT i
END SUB


Next, we cater for a case of pieceno being 0. When that happens, you print a single brown space, to indicate that this section of rod has no disks, and it's all rod.
SUB drawpiece (pieceno)
COLOR 0, 0
FOR i = 1 TO (5 - pieceno)
    PRINT " ";
NEXT i

IF pieceno = 0 THEN
    COLOR 0, 6
    PRINT " ";
ELSE

END IF

COLOR 0, 0
FOR i = 1 TO (5 - pieceno)
    PRINT " ";
NEXT i
END SUB


And if pieceno is not 0, which means there is a disk on that section of rod, you draw the element of the piece array referenced by pieceno. For the color of the disk, we'll just use the color that pieceno represents in the QBasic color table. piece(1) will be blue, piece(2) will be green, piece(3) will be yellow and piece(4) will be red.
SUB drawpiece (pieceno)
COLOR 0, 0
FOR i = 1 TO (5 - pieceno)
    PRINT " ";
NEXT i

IF pieceno = 0 THEN
    COLOR 0, 6
    PRINT " ";
ELSE
    COLOR 0, pieceno
    PRINT piece(pieceno);
END IF

COLOR 0, 0
FOR i = 1 TO (5 - pieceno)
    PRINT " ";
NEXT i

END SUB


After that, reset the colors.
SUB drawpiece (pieceno)
COLOR 0, 0
FOR i = 1 TO (5 - pieceno)
    PRINT " ";
NEXT i

IF pieceno = 0 THEN
    COLOR 0, 6
    PRINT " ";
ELSE
    COLOR 0, pieceno
    PRINT piece(pieceno);
END IF

COLOR 0, 0
FOR i = 1 TO (5 - pieceno)
    PRINT " ";
NEXT i

COLOR 15, 0
END SUB


Now let's test the subroutine!
drawpiece 1
drawpiece 2
drawpiece 3
drawpiece 4
drawpiece 0


As expected.


Now delete the test statements. And write a new subroutine, drawstack(). This one will leverage off the subroutine, drawbase(), that we created earlier.
SUB drawbase (stackno)
COLOR 0, 6
FOR i = 1 TO 3
    PRINT " ";
NEXT

PRINT stackno;

FOR i = 1 TO 3
    PRINT " ";
NEXT

COLOR 15, 0
END SUB

SUB drawstack ()

END SUB


The entire stack contains three bases and three rods. Counting the bases, it will be 6 spaces high. So create a For loop iterating from 1 to 6. Within it, send the cursor to a y value of (i + 1) and an x value of 2. The x value is to allow some space at the left side of the screen.
SUB drawstack ()
FOR i = 1 TO 6
    LOCATE i + 1, 2
NEXT i
END SUB


Define another For loop within it, iterating from 1 to 3. j will represent each base and rod.
SUB drawstack ()
FOR i = 1 TO 6
    LOCATE i + 1, 2

    FOR j = 1 TO 3

    NEXT j
NEXT i
END SUB


If we're at the top of the stack (i.e, i is 1), whether or not there are disks, there will be only the rod showing. So call drawpiece(), and use 0 as an argument.
SUB drawstack ()
FOR i = 1 TO 6
    LOCATE i + 1, 2

    FOR j = 1 TO 3
        IF i = 1 THEN
            drawpiece 0
        ELSE

        END IF
    NEXT j
NEXT i
END SUB


If i is not 1, then check if i is 6, which means you're now drawing the bottom of the stack. If so, draw the base by calling drawbase() and pass in j as an argument.
SUB drawstack ()
FOR i = 1 TO 6
    LOCATE i + 1, 2

    FOR j = 1 TO 3
        IF i = 1 THEN
            drawpiece 0
        ELSE
            IF i = 6 THEN
                drawbase j
            ELSE

            END IF
        END IF
    NEXT j
NEXT i
END SUB


If i is not 1 or 6, then... well, for now let's just use drawpiece() with an argument of 0 to show what the entire setup would look like without disks.
SUB drawstack ()
FOR i = 1 TO 6
    LOCATE i + 1, 2

    FOR j = 1 TO 3
        IF i = 1 THEN
            drawpiece 0
        ELSE
            IF i = 6 THEN
                drawbase j
            ELSE
                drawpiece 0
            END IF
        END IF
    NEXT j
NEXT i
END SUB


Now call drawstack().
drawstack

SUB drawpiece (pieceno)
COLOR 0, 0
FOR i = 1 TO (5 - pieceno)
    PRINT " ";
NEXT i


OK, this is a little off. Let's fix it...


In drawpiece(), add a black space after drawing.
SUB drawpiece (pieceno)
COLOR 0, 0
FOR i = 1 TO (5 - pieceno)
    PRINT " ";
NEXT i

IF pieceno = 0 THEN
    COLOR 0, 6
    PRINT " ";
ELSE
    COLOR 0, pieceno
    PRINT piece(pieceno);
END IF

COLOR 0, 0
FOR i = 1 TO (5 - pieceno)
    PRINT " ";
NEXT i

COLOR 0, 0
PRINT " ";

COLOR 15, 0
END SUB


Do the same for drawbase().
SUB drawbase (stackno)
COLOR 0, 6
FOR i = 1 TO 4
    PRINT " ";
NEXT

PRINT stackno;

FOR i = 1 TO 4
    PRINT " ";
NEXT

COLOR 0, 0
PRINT " ";

COLOR 15, 0
END SUB


Ladies and gentlemen, your rods and bases!


Next

We need to put those disks on the first rod. We already created the piece array, and the drawpiece() subroutine. We'll take care of placement in the next part of this tutorial.

Tuesday, 8 May 2018

The Myth Of Multi-tasking

If you've been out job-hunting, you would probably have seen job ads that want people who can do several things at once.

"Ability to Multi-task" is probably one of the most common requirements a job applicant will see on job applications these days. It's unfortunate because I don't think "multi-tasking" means quite the same thing most employers think it means. To them, it's a cheap pretty way of saying "I should be able to load you with several different things at one go, and you should still be able to perform."

Work, slave!

Techies, however, know that even with ultra-fast computers, there is no such thing. Computers only seem capable of doing several things at one go because they are task-switching - carrying out bits and pieces of one process and then switching to the next process - very rapidly.

The next closest thing would be multi-processing. That's when one computer has two CPUs and they're using these processing units to simultaneously do stuff. This effectively means the multi-processing computer has two brains. There's no human being on God's green earth that has two brains, and no, I'm not counting cojoined twins!

The Good News

Human beings can multi-task. If, and only if, those tasks do not engage higher cognitive functions. In other words, if something comes as naturally as breathing (such as walking, swallowing and yes, breathing), all it really requires is your subconscious.

For example, let's say you're lost along a street. You're checking the road signs and Google Maps on your phone at the same time. Or are you? How many things are you actually doing at the same time?

You're breathing, because like most living organisms, you need a constant supply of oxygen.

You're walking, and instinctively avoiding collision with nearby objects.

You're checking out road signs and storing the information in short-term memory.

You're looking at the display on Google Maps to see if the information correlates with the information in your short-term memory.

Four things at one go! Or is it? Walking and breathing are managed by your subconscious.

And you're task-switching between reading road signs and checking Google Maps. You're not doing those two things simultaneously! The moment you start looking at your phone, you're no longer reading road signs. And vice versa. But no matter which you're doing, you're still walking and breathing.

The Bad News

It's the sucky human limitation that prohibits us from giving several different things our undivided attention all at once. (Yes, never mind unrealistic expectations from employers. It's our fault entirely. Curse our limitations!)

If you are talking to a customer, you can't be writing code at the same time. You're either doing one or the other, or doing a piss-poor job of both. Similarly, you can't be debugging a program while backing up a database, even if both happen to be your job. You have only one brain, and at any one point in time, you are doing only one thing.

OK, so let's call it "task-switching", then!

Call it whatever you want, the fact remains that this affects performance negatively. Hey, don't take my word for it. Try this experiment.

See the table below? Each column has a different formula. Start with the number 5. For every row (other than the first, which uses the number 5), use the result of the previous row in that column in place of y.

The first three rows has been done for you. Now do this for, say, ten rows.

A B C
(y + 5) (y x 2) (y + 10)
1. 10 10 15
2. 15 20 25
3. 20 40 35
4. ? ? ?
5. ? ? ?
6. ? ? ?
7. ? ? ?
8. ? ? ?
9. ? ? ?
10. ? ? ?


But wait... first, try filling all ten rows of Column A, then all ten rows of Column B, then all ten rows of Column C. Easy, right?

Now try filling the first row of Column A, the first row of Column B, and the first row of Column C, and so on until you complete ten rows. Not so easy this time, was it?

The point here is to illustrate that when your brain switches tracks, there's a cost in performance. You have to load a new formula in your head every time you go from Column A to Column B, then Column C. That's (3 x 10 = 30) thirty switches.

Whereas if you just did all ten rows in Column A first, you only had to apply one formula for ten rows, then another formula for the next ten rows in Column B, and another for the next ten rows in Column C. That's only three formulas.

Which method ultimately strikes you as more efficient? To a computer, there seems to be no difference because they perform that fast anyway. But it's your brain we're talking about. The human brain.

Even in software development, when devs are being assigned a list of bugs to fix, they're told to work on only one thing at a time. Anything else is just asking for trouble.

Finally...

I have no problem with employers wanting their employees to handle a lot of stuff at one go. After all, if your employers don't try to exploit you at all, what kind of employers would they be? The only problem is when they think that this shouldn't and doesn't affect performance.

Because it really fucking does.

And also with employers who insist that their employee should be able to "multi-task" and still perform superbly, yet raise all hell when they catch you making a private call on your phone while typing a document. Make up your bloody minds, please! You can't have your cake and eat it too.

Oh wait, you're the almighty boss. I guess you can, eh?


Simultaneous salutations,
T___T

Friday, 4 May 2018

A Company, Not A Kindergarten

There is a troubling culture among the more traditional types of companies today, a mentality I like to call the Chinese Towkay Syndrome.

Back in the day, making a living was hard, and if someone found steady employment, the boss of the company inevitably felt that they were doing their employees a favor by letting them stay on. The "favor" part was especially hard to argue against though, since many of these towkays were in the habit of employing family or friends in positions they were woefully underqualified for. Believe it or not, this practice still exists today!

The positive aspect of this was that employers felt that they were responsible for the careers and lives of their employees, and thus acted with caution and prudence.

King of the castle.

The negative aspect, of course, was that they let this go to their heads. They started thinking that they needed to rule everything with an iron hand, and that their word was law. They felt the need to ensure that every minute that the company paid for the worker's time, was spent working. Being at the head of a company, even if that company in question is a tiny kuching kurap specimen, does lend itself to a certain amount of hubris. (Though personally, if my company was still a minnow in this tiny pond after thirty years, I'd be a little less cocky about being a boss.) That attitude, unfortunately, trickles down to senior management fairly often.

This syndrome seems to be especially prevalent among the kinds of companies that have lasted the last few decades or so, and among the Chinese. Hence the term Chinese Towkay Syndrome. This is probably unfair, of course. Malays and Indians are not immune to hubris. Hubris is not a racial thing. But Chinese make up the majority on this island, therefore the term sticks.

So, anyway, this tends to manifest itself in...

Treating employees like children

Ever had your clock-in and clock-out times monitored? That's normal.

Ever had your lunch breaks counted, and been reprimanded if you consistently took longer than an hour? That's normal too. How much time does one reasonably need to get food into your body, right?

Having toilet breaks monitored too? That's when a line has been crossed. That's when you've started treating your employees like wayward children who will abuse the system at every opportunity if you don't lay down the law. Seriously, some people need a longer time to move their bowels. Are you planning to factor that into the year-end appraisal?

That's not the only example, of course.

As desktop support, I've had to write scripts for HR to determine who's taking MC or urgent leave on Mondays, Fridays, before or after public holidays, and flag the people doing so for possible misuse of benefits. Or monitor the emails of employees to ensure that they aren't involved in idle chit-chat.

LUMPAR - "Let's Understand, Most People Are Ridiculous." I coined that acronym sometime last year, and it couldn't be more apt now. Nothing screams "ridiculous" louder than spending time and resources on making sure people spend every minute typing at the keyboard... especially if the people in question are web developers. Forget everything you've watched in the movies. This is not how web development works. Typing code into the system only accounts for about ten percent of the entire process - something the typical layperson doesn't know. Which, of course, is one of the many reasons why I never want to work for laypeople again. But that's another story for another time.

Treating employees like wayward
children.

Here's the thing. You are hiring people do do certain jobs that require a certain amount of experience and expertise. If you treat your employees like children who must be constantly monitored for every little thing, they are going to act like children. They are going to find every loophole in your precious system and abuse the hell out of it. Hey, you already think they are children. You think so little of their professionalism that you feel the need to micro-manage them. What have they got to lose? Your respect?

Why not try trusting them to be adults instead, and act like professionals? It's a company you're running, not a kindergarten. What's the point of demanding that they work? Just so you can feel the company is getting its money' worth? That's dumb. It's ultimately output you're paying for, isn't it? There's no point in employees coming in early and leaving late, and constantly keeping their eyes glued to the computer screen and fingers making contact with the keyboard, if the work isn't done. Obsessing over input rather than output paints an unflattering picture of your ability to prioritize things that matter.

My home as an example

Apparently, the workplace is not the only place Singaporeans display symptoms of the Chinese Towkay Syndrome. Singaporeans are famous for being especially nit-picky where rental is concerned. We have something of a reputation for being the worst landlords (and landladies) ever.

Last year, my tenant moved out because he had finally completed the purchase of his own apartment. Before he left, he shook my hand and told me I was probably the most fuss-free landlord he had ever encountered. I was flattered, but thought that was probably a coincidence.

Coincidence? Oh hell, no. I was about to learn otherwise.

Over the weeks, I reviewed a few prospective tenants. One of them asked me how many times a week they could use the washing machine and the air-conditioning. I gave him a funny look that suggested I thought it a crazy question. What, do I look like I have the time to monitor how many times a week they use the machine or turn on the air-conditioning? I'm a working professional, for God's sake. I have better things to do. And constantly watching people to ensure they don't use any more water or electricity than they're supposed to, is a lot of work. It's a time-consuming exercise that will save me maybe a measly thirty bucks a month.

I also got asked some things like whether or not they were allowed to cook, bring friends over, smoke, and whatnot. My response was, typically, as long as they didn't trash the place or burn it down, we were good.

Later on, I even got asked whether they could put stuff on the shelves in the bathroom. My response, by that time, was an exasperated sigh. Look man, if you don't put stuff on the shelves, what else would you use those goddamn shelves for?

By then, the visitors had a pretty good idea I didn't suffer from Chinese Towkay Syndrome. Unfortunately, it appeared that a lot of Singaporeans did. Somehow, after taking so much crap from their bosses at work, being the boss of their own homes seemed to have gone to their heads. Everything I took for granted and relied on my prospective tenants to exercise their own judgement in, other landlords insisted on controlling. I was told that my relaxed stance towards tenant behavior was almost unheard of. I kid you not; there were some prospective tenants who didn't turn up for viewing as soon as they learned I was Singaporean. Yes, our reputation is that bad.

Net result: all of the prospective tenants expressed interest after the visit. They didn't even quibble over the price!

Almost a year later, in addition to paying the rent on time, my tenants are helping me sweep the floor, take out the trash, and clean the bathroom. Without being asked! See what happens when you treat people like adults? They exceed your expectations.

To conclude...

I'm aware that not all cases turn out this happily. People are a mixed bag. My sister thinks that I just got lucky and found a bunch of guys that, after having horrible experiences with other landlords, learned to appreciate a good thing when they saw it. She may have a point there. But I firmly believe that the more you try to control people, the harder it gets for everyone involved.

Long story short: Learn to let go, towkays. Lighten up. Nobody's impressed.

Till next time, stay boss-sitive!
T___T