Monday, 17 February 2020

App Review: Paper Wings

Chirpy, cheery and fun - these are just a few of the adjectives that can be used to describe Paper Wings, a mobile game from Fil Games that's billed as an environmentally-conscious time-filler.



Paper Wings is a basic left-and-right console game with very pretty effects centered around origami avians on origami landscapes; hence the name. Other than that, there's really nothing much else to say about it - gameplay is really that basic.

The Premise

You control a bird. The bird flies around, avoiding obstacles and collecting enough points to advance to the next level, where things get even more dangerous.



The Aesthetics

Paper Wings is a thing of beauty, and I don't say this lightly. Everything is rendered in brilliant color and surreal landscapes. And the truly awesome thing is the simplicity of the entire setup. For a game so basic, it certainly is well put-together.

The Experience

It's tranquil, its cute, and it's light-hearted gaming activity unless you take it too seriously. Paper Wings is as much about ecological awareness as it is about scoring points.

The Interface

Left, right. Tap and hold to rotate. Release to let the bird glide using momentum. Takes a while to get the hang of.

What I liked

I could spend all day raving about the sheer aesthetic beauty of this game. Suffice to say, Paper Wings has gorgeous backdrops and simple but breathtaking animation. The movement of the birds can be stiff, but that can easily be excused by the fact that they are supposed to be paper birds. However, the way they're able to glide through the screen is just magical in its implementation.


The sound effects - birds chirping and cawing, wings flapping, wind whooshing, water splashing - are masterfully done. It has an incredibly calming effect. There's even a challenge based upon sound effects! And speaking of that...

Challenges are creatively done. Some of them require you to explore the interface and discover winning combinations.



The variety of the birds on offer is mind-boggling. Once you accumulate a certain number of coins or achieve a certain number of tasks, you unlock a new bird. My personal favorite is the crow.

Little touches. The interface interacts with you in unexpected ways. It's really cute.

There's even plenty of ecological commentary within the app, along with how we can preserve avian wildlife. A mobile game with a climate conscience! Wow!

What I didn't


Emotional manipulation. The bird says it's missed you if you're gone long enough. Nice touch, I guess, but I really could do without the feels, dammit!

Video-watching is thankfully optional if you don't mind missing out on certain birds and replaying levels from scratch. Because their videos are pretty grating.

Having the entire level lost as soon as even one coin sinks into the water seems to me a bit too draconian. This pretty much means constant replaying of levels (and video-watching if you don't want to replay a level) if you get a less-than-perfect score.


There's a bonus associated with catching a coin immediately after it descends from the sky, but it's annoyingly inconsistent as to how it's implemented. Sometimes you need to catch it as soon as it appears, other times you can get away with a one-second delay.

Conclusion

This is a great app to have in your phone if you like a nice soothing distraction. Gameplay isn't groundbreaking exactly, but you'll get plenty of kicks out of this nonetheless. Paper Wings is a little more than your run-of-the-mill mobile game - it's a labor of love, and it shows.

My Rating

6.5 / 10

This game is totally fly!
T___T

Wednesday, 12 February 2020

Web Tutorial: Tic-tac-toe, Valentine's Day Edition (Part 2/2)

The current level of intelligence used in this JavaScript game is zero. The computer takes its turn with no strategy whatsoever, only using available squares and a random number as criteria. We can leverage on the existing code to determine strategic actions for the computer to take, rather than random ones.

In the game object, at the getFinalSquare() method. It should accept a parameter string, mark.
getRandomBlankSquare: function()
{
    var blanks = this.board.filter(function (x) {return x.value == ""});

    if (blanks.length == 0)
    {
        return null;
    }
    else
    {
        var rand = this.getRandomNumber(0, blanks.length - 1);
        return this.board.findIndex(function (x) {return x.square == blanks[rand].square});   
    }
},
getFinalSquare: function(mark)
{

},
nextTurn: function()
{
    this.turn =  (this.turn == "x" ? "o" : "x");
    user.myTurn();
},


It will return null by default; meaning, no strategic path was found. As in the getRandomBlankSquare() method, you first check for blank squares. If there are no blank squares, return null. If there are, also possibly return null.
getFinalSquare: function(mark)
{
    var blanks = this.board.filter(function (x) {return x.value == ""});

    if (blanks.length == 0)
    {
        return null;
    }
    else
    {
        return null;
    }
},


If there are blank squares, that means there is a possibility that a strategic path can be found. For this, we need to iterate through the game object's winPatterns array.
getFinalSquare: function(mark)
{
    var blanks = this.board.filter(function (x) {return x.value == ""});

    if (blanks.length == 0)
    {
        return null;
    }
    else
    {
        for (var i = 0; i < this.winPatterns.length; i++)
        {

        }

        return null;
    }
},


Within the For loop, define three variables; marked, filled - both set to 0 - and finalSquare, which is null by default.
getFinalSquare: function(mark)
{
    var blanks = this.board.filter(function (x) {return x.value == ""});

    if (blanks.length == 0)
    {
        return null;
    }
    else
    {
        for (var i = 0; i < this.winPatterns.length; i++)
        {
            var marked = 0;
            var filled = 0;
            var finalSquare = null;
        }

        return null;
    }
},


Iterate through each element of the winPatterns array. Because each element is in turn an array of three elements, remember? If the value is occupied by mark, increment marked. If the value is occupied (whether by mark or anything else), increment filled. If the value is blank, set finalSquare to the value of that square.
getFinalSquare: function(mark)
{
    var blanks = this.board.filter(function (x) {return x.value == ""});

    if (blanks.length == 0)
    {
        return null;
    }
    else
    {
        for (var i = 0; i < this.winPatterns.length; i++)
        {
            var marked = 0;
            var filled = 0;
            var finalSquare = null;

            for (j = 0; j < 3; j++)
            {
                if (this.winPatterns[i][j].value == mark) marked++;
                if (this.winPatterns[i][j].value != "") filled++;
                if (this.winPatterns[i][j].value == "") finalSquare = this.winPatterns[i][j].square;
            }
        }

        return null;
    }
},


Now outside of the inner For loop and in the main For loop, check if marked is 2 and filled is less than 3. That basically means that for that winPattern, two slots have been filled by the user's mark and one remains for victory. Once that's established, there's no need to search further - just return the index value of that square.

If no strategic paths are found at all, the method will return null.
getFinalSquare: function(mark)
{
    var blanks = this.board.filter(function (x) {return x.value == ""});

    if (blanks.length == 0)
    {
        return null;
    }
    else
    {
        for (var i = 0; i < this.winPatterns.length; i++)
        {
            var marked = 0;
            var filled = 0;
            var finalSquare = null;

            for (j = 0; j < 3; j++)
            {
                if (this.winPatterns[i][j].value == mark) marked++;
                if (this.winPatterns[i][j].value != "") filled++;
                if (this.winPatterns[i][j].value == "") finalSquare = this.winPatterns[i][j].square;
            }

            if (marked == 2 && filled < 3)
            {
                return this.board.findIndex(function (x) {return x.square == finalSquare});
            }
        }

        return null;
    }
},


Let's get back to the myTurn() method of the user object. We need to expand on this. Basically, before attempting to get a random blank square, the program must first check for a strategic move that can be made.

First, run the getFinalSquare() method using "x" as the argument. This searches for a "win" scenario where "x" would win the game. That's the first priority. If no "win" scenario exists, the next thing to do is try to stop user "o" from winning. So again, we run the getFinalSquare() method, but this time using "o" as an argument. And if there is no "win: scenario for "o" either, just use a random blank square.
setTimeout
(
    function()
    {
        var finalSquare = game.getFinalSquare("x");

        if (finalSquare == null)
        {
            finalSquare = game.getFinalSquare("o");

            if (finalSquare == null)
            {
                var randomBlankSquare = game.getRandomBlankSquare();

                if (randomBlankSquare == null)
                {
                    document.getElementById("user_" + game.turn + "_face").innerHTML = "&#9786";
                    document.getElementById("user_" + game.turn + "_text").innerHTML = "Looks like it's a draw.";
                    game.started = false;
                }
                else
                {
                    user.takeTurn(randomBlankSquare, "x");
                }
            }
        }
    },
    1000
)


Here, we set the Else blocks to take the appropriate turn if there are strategic paths to take.
setTimeout
(
    function()
    {
        var finalSquare = game.getFinalSquare("x");

        if (finalSquare == null)
        {
            finalSquare = game.getFinalSquare("o");

            if (finalSquare == null)
            {
                var randomBlankSquare = game.getRandomBlankSquare();

                if (randomBlankSquare == null)
                {
                    document.getElementById("user_" + game.turn + "_face").innerHTML = "&#9786";
                    document.getElementById("user_" + game.turn + "_text").innerHTML = "Looks like it's a draw.";
                    game.started = false;
                }
                else
                {
                    user.takeTurn(randomBlankSquare, "x");
                }
            }
            else
            {
                user.takeTurn(finalSquare, "x");
            }
        }
        else
        {
            user.takeTurn(finalSquare, "x");
        }
    },
    1000
)


Now try this! If you place your token in the center of the board hoping to get a diagonal row...


...this should happen!


And if you try to get a row while the computer is already on its way there...


...this happens!


Happy Valentine's Day, you crazy couples!

Celebrate love, romance, and all that shit. Just seize the day and enjoy the memories!

X my ♥,
T___T

Monday, 10 February 2020

Web Tutorial: Tic-tac-toe, Valentine's Day Edition (Part 1/2)

Can you feel the romance in the air?! Valentine's Day is coming up, lovebirds, and boy do I have the web tutorial for you today.

Remember last year when we made this neat little Tic-tac-toe game in HTML, CSS and JavaScript? Today, we will revisit it and improve on it. We'll first put a little Valentine's Day theme to it, and then we'll even implement some artificial intelligence. All while leveraging on the existing code.

First, the colors!

It wouldn't be Valentine's Day if we didn't saturate this sucker with unhealthy doses of red and pink, so let's make some changes to the CSS. That's right... turn the buttons red, and pink on a mouseover.
.btn
{
    font-family: "Comic Sans MS";
    font-size: 2em;
    display: block;
    width: 5em;
    height: 1.5em;
    border-radius: 0.5em;
    border: 0px solid;
    background-color: #FF0000;
    color: #FFFFFF;
    margin: 1em auto 0 auto;
}

.btn:hover
{
    background-color: #FF8888;
    color: #FF0000;
}

A good beginning...

The board needs a little makeover as well. Lighter shades of pink should do it.
#board_container
{
    width: 330px;
    height: 330px;
    margin: 0 auto 0 auto;
    background-color: #FFCCCC;
    padding-top: 10px;
    padding-left: 10px;
}

.square
{
    width: 100px;
    height: 100px;
    margin-right: 10px;
    margin-bottom: 10px;
    float: left;
    outline: 1px solid #FFEEEE;
    background-color: #FFFFFF;
    cursor: pointer;
}


And for good measure, we'll even change the colors of the users.
#user_x_container
{
    color: #FF0000;
}

#user_o_container
{
    color: #FF8888;
}


Ooh, sappy, eh?


Now, for the tokens, instead of "X" and "O", we'll use hearts of a different color.
{
    content: "\2764";
    color: #FF0000;
}

.o:before
{
    content: "\2764";
    color: #FF8888;
}


And upon winning, this shade of pink should contrast nicely with both colors!
.win
{
    background-color: #FF4444;
}


Here you go.


Oh hey, here's a cheeky touch.
<h1>Let's play Tic-tac-t&hearts;e!</h1>


Damn, so cheesy.


Some randomly fun features

I think it's a wee bit boring to have the facial expressions unchanged, and the messages look repetitive. So let's introduce a random element into all this.

Add the getRandomRemark() method into the game object.
var game =
{
    turn: "x",
    started: true,
    board:
    [
        {square: "a", value: ""},
        {square: "b", value: ""},
        {square: "c", value: ""},
        {square: "d", value: ""},
        {square: "e", value: ""},
        {square: "f", value: ""},
        {square: "g", value: ""},
        {square: "h", value: ""},
        {square: "i", value: ""}
    ],
    winPatterns:
    [
        [
            {square: "a", value: ""},
            {square: "b", value: ""},
            {square: "c", value: ""}
        ],
        [
            {square: "a", value: ""},
            {square: "d", value: ""},
            {square: "g", value: ""}
        ],
        [
            {square: "a", value: ""},
            {square: "e", value: ""},
            {square: "i", value: ""}
        ],
        [
            {square: "b", value: ""},
            {square: "e", value: ""},
            {square: "h", value: ""}
        ],
        [
            {square: "c", value: ""},
            {square: "f", value: ""},
            {square: "i", value: ""}
        ],
        [
            {square: "c", value: ""},
            {square: "e", value: ""},
            {square: "g", value: ""}
        ],
        [
            {square: "d", value: ""},
            {square: "e", value: ""},
            {square: "f", value: ""}
        ],
        [
            {square: "g", value: ""},
            {square: "h", value: ""},
            {square: "i", value: ""}
        ]
    ],
    init: function()
    {
        this.started = true;
        this.turn = "x";

        for (var i = 0; i < this.board.length; i++)
        {
            this.board[i].value = "";
        }

        for (var i = 0; i < this.winPatterns.length; i++)
        {
            for (var j = 0; j < this.winPatterns[i].length; j++)
            {
                this.winPatterns[i][j].value = "";
            }
        }

        for (var i = 0; i < 9; i++)
        {
            document.getElementById("square" + i).className = "square";
        }

        var texts = document.getElementsByClassName("text");
        for (var i = 0; i < texts.length; i++)
        {
            texts.innerHTML = "";
        }
    },
    play: function()
    {
        document.getElementById("btnPlay").style.display = "none";
        document.getElementById("btnQuit").style.display = "block";
        document.getElementById("game_container").style.display = "block";
        this.nextTurn();
    },
    quit: function()
    {
        this.init();
        document.getElementById("btnQuit").style.display = "none";
        document.getElementById("btnPlay").style.display = "block";
        document.getElementById("game_container").style.display = "none";
    },
    getRandomNumber: function(min, max)
    {
        return Math.floor((Math.random() * (min - max + 1)) + max);
    },
    getRandomBlankSquare: function()
    {
        var blanks = this.board.filter(function (x) {return x.value == ""});

        if (blanks.length == 0)
        {
            return null;
        }
        else
        {
            var rand = this.getRandomNumber(0, blanks.length - 1);
            return this.board.findIndex(function (x) {return x.square == blanks[rand].square});   
        }
    },
    nextTurn: function()
    {
        this.turn =  (this.turn == "x" ? "o" : "x");
        user.myTurn();
    },
    checkWin: function(mark)
    {
        var findSquare;

        for (var i = 0; i < this.winPatterns.length; i++)
        {
            if (this.winPatterns[i][0].value == mark && this.winPatterns[i][1].value == mark && this.winPatterns[i][2].value == mark)
            {
                for (var j = 0; j < 3; j++)
                {
                    var winPatterns = this.winPatterns;
                    findSquare = this.board.findIndex(function (x) {return x.square == winPatterns[i][j].square});
                    document.getElementById("square" + findSquare).className += " win";                           
                }

                return true;
            }   
        }

        return false;
    },
    getRandomRemark: function()
    {
   
    }               
}


Define a variable, remarks. It will be an array of objects.
getRandomRemark: function()
{
    var remarks =
    [

    ];
}   


Each object has the properties face and text. face is the HTML symbol used to accompany the remark, and text is the remark itself.
getRandomRemark: function()
{
    var remarks =
    [
        {face: "&#9786;", text: "Looks like it's my turn!"},
        {face: "&#9854;", text: "Brilliant move!"},
        {face: "&#9785;", text: "Ouch!"},
        {face: "&#9863;", text: "Um..."},
        {face: "&#9863;", text: "This is hard."},
        {face: "&#9786;", text: "Let me see..."},
        {face: "&#9786;", text: "I'm gonna cream you!"},
        {face: "&#9786;", text: "Ooh. Nice."}
    ];
}


Grab a random number using the existing getRandomNo() method, passing in 0 and the length of remarks minus 1, as arguments, and assign the value to the variable rand.
getRandomRemark: function()
{
    var remarks =
    [
        {face: "&#9786;", text: "Looks like it's my turn!"},
        {face: "&#9854;", text: "Brilliant move!"},
        {face: "&#9785;", text: "Ouch!"},
        {face: "&#9863;", text: "Um..."},
        {face: "&#9863;", text: "This is hard."},
        {face: "&#9786;", text: "Let me see..."},
        {face: "&#9786;", text: "I'm gonna cream you!"},
        {face: "&#9786;", text: "Ooh. Nice."}
    ];

    var rand = this.getRandomNumber(0, remarks.length - 1);
}


Finally, return the array element pointed to by rand.
getRandomRemark: function()
{
    var remarks =
    [
        {face: "&#9786;", text: "Looks like it's my turn!"},
        {face: "&#9854;", text: "Brilliant move!"},
        {face: "&#9785;", text: "Ouch!"},
        {face: "&#9863;", text: "Um..."},
        {face: "&#9863;", text: "This is hard."},
        {face: "&#9786;", text: "Let me see..."},
        {face: "&#9786;", text: "I'm gonna cream you!"},
        {face: "&#9786;", text: "Ooh. Nice."}
    ];

    var rand = this.getRandomNumber(0, remarks.length - 1);
    return remarks[rand];   
}


In the myTurn() method of the user object, remove this line.
myTurn: function()
{
    document.getElementById("user_x_text").innerHTML = "";
    document.getElementById("user_o_text").innerHTML = "";
    //document.getElementById("user_" + game.turn + "_text").innerHTML = "My turn!";


Instead, declare the variable remark and set it to the object returned by calling the getRandomRemark() method of the game object.
myTurn: function()
{
    document.getElementById("user_x_text").innerHTML = "";
    document.getElementById("user_o_text").innerHTML = "";
    //document.getElementById("user_" + game.turn + "_text").innerHTML = "My turn!";

    var remark = game.getRandomRemark();


Then use the face and text properties to populate these placeholders.
myTurn: function()
{
    document.getElementById("user_x_text").innerHTML = "";
    document.getElementById("user_o_text").innerHTML = "";
    //document.getElementById("user_" + game.turn + "_text").innerHTML = "My turn!";

    var remark = game.getRandomRemark();
    document.getElementById("user_" + game.turn + "_text").innerHTML = remark.text;
    document.getElementById("user_" + game.turn + "_face").innerHTML = remark.face;


Also, in your HTML, add ids for these placeholders.
<div id="game_container">
    <div id="user_x_container" class="player">
        <div class="token" id="user_x_face">&#9786;</div>
        <div id="user_x_text" class="text"></div>
    </div>

    <div id="board_container">
        <div id="square0" class="square" onclick="user.takeTurn(0, 'o');"></div>
        <div id="square1" class="square" onclick="user.takeTurn(1, 'o');"></div>
        <div id="square2" class="square" onclick="user.takeTurn(2, 'o');"></div>
        <div id="square3" class="square" onclick="user.takeTurn(3, 'o');"></div>
        <div id="square4" class="square" onclick="user.takeTurn(4, 'o');"></div>
        <div id="square5" class="square" onclick="user.takeTurn(5, 'o');"></div>
        <div id="square6" class="square" onclick="user.takeTurn(6, 'o');"></div>
        <div id="square7" class="square" onclick="user.takeTurn(7, 'o');"></div>
        <div id="square8" class="square" onclick="user.takeTurn(8, 'o');"></div>
    </div>

    <div id="user_o_container" class="player">
        <div class="token" id="user_o_face">&#9786;</div>
        <div id="user_o_text" class="text"></div>
    </div>
</div>


Before I forget, also populate the "face" placeholder when the game is won.
if (game.checkWin(mark))
{
    document.getElementById("user_" + mark + "_face").innerHTML = "&#9786";
    document.getElementById("user_" + mark + "_text").innerHTML = "I win!";
    game.started = false;
}
else
{
    game.nextTurn();
}


Oh, ho! Now things are getting interesting!



Next

Let's simulate some intelligence in this game!

Thursday, 6 February 2020

Spot The Bug: The Calendar Conundrum

Hello and welcome! It's time for another episode of Spot The Bug!

I know you're
out there.

Ruby is a nice language to use with a syntax that is smooth as silk, but surprise, surprise... you can get code bugs in Ruby just as easily as any other language.

Here's what happened when I tried to use Ruby to write a script that would display a calendar view for the current month. For the record, it was this month of this year - February 2020.

In here, calStr is a list of dates, each one separated by a tab, and a break every time Saturday is encountered.
require "date"

nowDate = Date.today
currentDate = Date.new(nowDate.year, nowDate.month, 1)
lastDate = currentDate >> 1
lastDate = (lastDate - 1)
lastDay = lastDate.day

puts "Sun\tMon\tTue\tWed\tThu\tFri\tSat"
calStr = "";

for i in 1..lastDay do
    currentDate = Date.new(2020, 2, i)

    calStr = calStr + i.to_s + "\t"

    if (currentDate.wday == 6)
        calStr = calStr + "\n"
    end
end

puts calStr


Not too bad, right? Except the first day doesn't start on a Sunday, so the calendar is all wrong.


No problem! What I'll do is make another For loop before the For loop, to iterate from 0 to the first day. Then I'll print a tab character for every day before the first day!
puts "Sun\tMon\tTue\tWed\tThu\tFri\tSat"
calStr = "";

for i in 0..currentDate.wday do
    calStr = calStr + "\t"
end

for i in 1..lastDay do
    currentDate = Date.new(2020, 2, i)

    calStr = calStr + i.to_s + "\t"

    if (currentDate.wday == 6)
        calStr = calStr + "\n"
    end
end

What went wrong

Um... nope. For some reason, now the first day, 1, was out of alignment. But everything else was in alignment.


This only meant one thing - the number of tabs before the first date, was wrong. There was an extra tab.

Why it went wrong

See, one of the nice things about Ruby is that it utilizes ranges. Instead of painstakingly typing out stuff like for (int i = 0; i < x; i++), you can simply go for i in 1..x do. 1..x is the range and you can do a lot more with that. Follow this link for more about Ruby ranges!

Anyway, where I went wrong was the range. Ruby range syntax goes like this...

If you want to go from 0 to 10, you use this.
0..10


If you want to go from 0 to 9, excluding 10, you use this. See the extra dot?
0...10


I only had two dots in my range 0..currentDate.wday. Therefore the script would churn out a tab character from 0 to the first weekday... including the first weekday! So even if the first weekday was on a Sunday, there would still be a tab in front of it!

How I fixed it

That's right, all it took was an extra dot. Which meant only the number of tabs from 0 (Sunday) to the first weekday of the month (Saturday), five in all, would be printed.
for i in 0...currentDate.wday do
    calStr = calStr + "\t"
end


Voila!


Moral of the story

It's usually the very tiny things that elude us. And in this case, it was literally a dot.

Till next time..............................
T___T

Sunday, 2 February 2020

Four Hundred Dollars Below Budget

My monthly budgeting follows a strict routine. Every month, my company pays me by transferring funds into Account A. Every month on the 25th, I transfer a thousand dollars into Account B from Account A, and all my personal spending draws from Account B. Now, I don't spend exactly a thousand a month, but it's close enough. Any amount I put in or withdraw from any of my accounts sends a message to my phone.

Yes, I'm a control freak like that. There was a time when I lived from paycheck to paycheck, and it's resulted in certain habits.

So imagine my consternation when recently, before performing my monthly funds transfer into Account B, I looked into Account B and found that there was an extra four hundred dollars just sitting there. Four hundred. Not forty. Where had it come from? None of my notifications showed an incoming transaction by that amount. Was there a bill I'd forgotten to pay? Had the Singapore Government inserted money into my account (some National Budget or other, maybe?) and somehow forgotten to tell me?

I'm rich!!!

People told me I was fretting over nothing. Extra money in your account is a good thing, they said. Better than less money. Just relax and enjoy it.

Utter rubbish. That's the simplistic mentality of laypeople who don't write code for a living.

You see, people tend to focus on the end result, but the process is just as important, if not more. If there is a desirable result and you want to replicate it, it stands to reason that you have to repeat the process under the same conditions. If you don't even know what process should be repeated or the conditions to repeat it under, how are you to replicate that result?

An Example In Code

Here's an example in JavaScript. Say I have two functions, a() and b(). They each accept a parameter, x. Now let me run the function a() using 2 as an argument.

a(2);


Now, if a()'s formula was this...
a(2);

function a(x)
{
    return x * 2;
}


The result would be 4. Because 2 x 2 is 4, right?
4


But what if the formula for b() was this? And then we ran b() using 2 as an argument?
b(2);

function a(x)
{
    return x * 2;
}

function b(x)
{
    return x * x;
}


The result would be the same. Because x is 2, thus it would still be (2 x 2 = 4)!
4


That's a very simple example of how an identical result does not equate to an identical formula. Obviously, x multiplied by itself is not the same formula as x multiplied by 2, unless x is 2. What if x wasn't 2? What if x was some other value, say, 10? Then the two formulae would yield very different results.

The Takeaway

Thus, when testing code, you test with a variety of input and in a variety of environments, to ensure that the formula is correct. Because if you get what appears to be the correct result while applying an incorrect formula, your code is still incorrect. And this will lead to further problems down the road.

Apply the correct formula.


As a professional who codes for a living, I've had to sometimes apply certain code-related principles to life. If you want to ensure that your results are favorable, you have to apply the correct process with the correct input. It was never an issue of whether there was more or less money in my account. People were missing the point by a mile. The issue was that I didn't know where that money came from. That made me uncomfortable. It was imperative that I understand why there was extra money in my account.

Finally...

So where did that money come from? The answer was a lot simpler than I'd imagined.

During those few months, I was so busy with work that I didn't have time to go out and spend money. No fancy dinners, no movies, no whatever. And I was certainly too busy to check and see how much money there was in Account B before the monthly funds transfer. Thus whatever money that was left over at the end of the month, just kept accumulating.

And when I finally found the time to look, the sum had grown to four hundred dollars. Since it wasn't a result of anybody transferring money to my account, I naturally never received any notification.

Looks like I'm not as savvy as I thought, eh?

Warm regard$,
T___T