Showing posts with label Programming. Show all posts
Showing posts with label Programming. Show all posts

Sunday, February 1, 2015

A few things I have learnt (Feb 2015 version)

In just about 4 months, it will officially be my 2nd year working in the games industry. I have been meaning to write about what I have learnt in my 1st year but since I was not doing any real development, I withheld such a post. Now that I was given a chance to handle bigger problems, I would like to think that it is nice to write something up here. So far I have had a hand in porting a game to a platform, created features for an existing game, had a hand in creating an app from ground up, among others tasks like maintaining servers.

There's a few things I have learnt and hopefully other developers will read this to get a rough gauge of what they can expect in a big game company, especially if you are working on maintaining social games. I'm going to leave the very technical stuff aside, like attempting to do unit testing or write safe code. These are more of things you do not really learn in school.

So here are 3 that I can think off the top of my head..

1) Follow existing conventions as much as possible.
This applies to everything. Code, excel documents, documentation, SQL scripts, etc. Usually, there isn't much of a written document on conventions; it is up to you to read what was done, understand why it was done this way, and follow. If there is a flaw or if you feel some way of doing things should be changed, complete your task first and bring it up to higher ups. ALWAYS discuss with the affected people if you are making changes, even if you are the lead. 

When the deadline is tight, there will not be time to do a full code review, so in a sense, everyone is responsible for their code. There WILL be legacy stuff that do not follow the conventions. These are basically stuff that went unchecked for whatever reason. Do not follow them, cut your future junior programmers some slack. When future programmers pick up your code and they start going 'is there a reason why this guy isn't follow convention?', it's going to eat up their time, and time is money here.

2) Readable code vs Optimized code
It is ultimately in the heart of every aspiring programmer to push for as much optimization as possible. But the amount of time it could take to develop it, plus the pain it might cost the next programmer to take over your code can be a high price to pay. General rule of thumb is, if the code has an acceptable speed, there isn't a need to optimize it. 

I cannot stress how important readable code is.  If you have to eat slightly more CPU or RAM just to make your code look nicer, I'd say go for it 100%. I have seen some optimized code that was a pain in the neck to decipher and could completely be avoided at no cost whatsoever. For example, I give you 4 variables to store your values in the database, but somehow you want to make use of only 1 variable to store 3 variables by using bit shifting/masking. Sure it looks damn cool if you are creating something for a school project, but the next programmer is going to take like 30 mins to figure out what you are doing AND why are you even doing it like that. 

The most frustrating conclusion any programmer want to make is "because the previous programmer wants to be cool shit". That's not cool at all.

Another good rule of thumb my colleague pointed out to me is to be able to write code such that it is understandable without comments. Comment ONLY when you are doing weird shit. Also, comment only WHY, not WHAT or HOW. WHAT or HOW should be inferred from your code. If you need to explain WHAT or HOW, time to refactor.  

3) Power must be controlled
Programmers are super powerful. I mean, they are the ones who knows (supposedly) the inner workings of every part of the project. It will come to the point where a workaholic programmer, for the sake of saving time, wants to do everything by himself so that his task is completed faster.  Really, to us programmers, adding an extra text to a properties folder is extremely trivial. Clicking on buttons to, say, generate assets, is also trivial.  

But when communication is difficult, like say your teammates don't work next to you, this can get out of hand. Even when we work next to each other, we had our own share of such problems. Committing text files is fine and all thanks to merging tools, but what about more complicated things like images, or even folder structures? What if the folder you are working on disappeared, or suddenly had files attempting to override your files because they have same name? It gets messy. At the worst scenario, you would have to delete your repository (because you cannot figure out the problem or because it is FUBAR) and re-setup your workspace, taking tons of time because of one innocent commit.

For such unstable pipelines, I think it is inevitable but to assign a go-to personnel in charge of it until a clean solution presents itself. Like no matter how inefficient it sounds, stability of the project is more important. You would rather 1 guy screw up, than a possibly 10 other guys screw up.




Saturday, September 6, 2014

[Programming] #6: Double pointer trick with Linked Lists.

If you are using C/C++, there is this really cool trick you can use when dealing with linked lists thanks to the existence of pointers. It saves you a bit of memory, computation power and also coding annoying special cases, so why not use it!

Honestly though, this isn't much of a 'trick'. It's more of understanding pointers themselves. Once you understand them, implementing this 'trick' becomes really trivial.

Say we have a simple linked list like so:
struct Node {
  Node(int tData) : data(tData), nextNode(NULL) {}
  int data; // some kind of data your node holds; I will just use integer in this case
  Node * nextNode; // pointer to the next node
};

// ... somewhere in your program... 
Node * linkedListHead = new Node(0);
Node * iterator = linkedListHead;
for ( int i = 1; i < 5; ++i ) {
  iterator->nextNode = new Node(i);
  iterator = iterator->nextNode;
}
This simply creates 5 nodes in a linked list that looks like this (I'm using its data to represent itself):

0 -> 1 -> 2 -> 3 -> 4 -> NULL

Simple enough.

So let's say we want to perform a really standard list operation: Removing an items from the list...say, the node containing 3. Usually there are 2 kinds of functions you can implement for removal: Removal by comparing data, and removal by the node itself. I will just use the former as an example and skim on the latter later.

So let's go: Removal by comparing data. The brute force algorithm is simple:
1) Find node 3
2) Get node 2
3) link node 2 to node 4 via node 3
4) delete node containing 3

However implementation is not so easy. Here's how it can get really messed up:
1) Find 3; Okay that's easy, loop until I get the node containing 3!
2) Find 2; Oh that's easy too I will again until I find 2...wait...

So that's the first alarm that sounded in your head. Why on earth should a search and destroy algorithm take O(N^2)? No, that shouldn't be the case BUT we can remedy this cleanly like so:
1) For every node, check next node.
2) If next node contains 3, link current node to node 3's next node (which is node containing 4).
3) Delete node containing 3.

That looks much better...except that it's not going to work if we are trying to remove the 1st node in the linked list. All of a sudden, we have an special case to code for. There are several ways to do this; one is to just code a special case, the other is to introduce a 'dummy' node at the start of the linked list and iterate from there (that might introduce special cases for other functions)...or we do the 'trick'!

OKAY, now we finally get to the 'trick'. The trick is to have your iterator be double pointers instead of single pointer. This means that we are iterating Node* instead of Node. Here's a snippet of the double pointer in action:

Node ** iterator = &linkedListHead;
while ( (*iterator) != NULL ) {
  if ( (*iterator)->data == 3 ) {
   Node * nodeAfter = (*iterator)->nextNode;
   delete (*iterator);
   (*iterator) = nodeAfter;
   break;  
  }
  iterator = &iterator->nextNode;
}
With this, you can see that there is no need for any special cases and also why the hell STD list's remove function takes in a wonky item called 'iterator' (which essentially holds a double pointer for just this reason!). It's so clean and elegant that it gives me chills ^_^

Well explaining this is going to delve into a crazy rabbit hole about pointers and stuff, so I'm just going to do what teachers do and say 'work it out on paper!'. This is actually good practice to strengthen your understanding of pointers. My advice is to draw every component in detail while you are figuring this out.

And if next time someone asks you what double pointers are for other than creating double array, this is a really solid and practical example to give.

Friday, July 25, 2014

[Programming] #5 Passing by value and reference, with a pinch of const

This is going to just be a alternate perspective of the good old "pass by values vs pass by reference" programming topic.

The motivation of writing this stems from me currently in a working environment with programmers coming from so many different backgrounds and when they land into a project in C++, they tend to get lost about this only because they are not used to it (these are good programmers who actually know their shit).

The other motivation is so that when I encounter programmers who do not completely understand this, I can just copy paste this to them and hopefully they understand.

Needless to say, I would like to thank Java, javascript, PHP and all those other languages for blurring the lines between passing by reference and passing by value. THANKS =/

Anyway. This is written with cocos2d-x classes.

There are 2 ways of how you would pass an object in C++ (I will used a more extreme example like CCArray):

void foo( CCArray array ) //pass by value

and
void boo( CCArray& rArray ) // pass by reference


In the first line, the object is passed by value. This means that every time you call the function foo(), it will literally take your original CCArray, create a copy of CCArray and copy *each and every* of the original's content into the copy before the function starts. Some humans usually call this 'piracy', and while the idea of piracy is 'nice' and all, it takes up more space and it takes time to copy.

In the worse case common scenario, you might write code like this:

class SomeClass {
public:
  foo(CCArray array);
private:
  m_array;
}

void SomeClass::foo( CCArray array ) {
  m_array = array; // wut
}

// somewhere else
SomeClass myClass;
CCArray array(); // pretend that there is 1000 objects in pArray
myClass.foo(pArray);

In this case, when you call 'myClass.foo(pArray)', it will first copy pArray into a separate CCArray before the start of the function.

Within the function, thanks to the awesome assignment operator in m_array = array, it will attempt to copy the copied CCArray 'array' into a new copy 'm_array'! (Double piracy!)

This is obviously not very good. The second copying is actually fine because we want our own copy of the CCArray, but the first copying process feels redundant. Why should we copy twice for one copy?

The alternative is to pass by reference:

void SomeClass::foo( CCArray& rArray ) {
  m_array = rArray ;
}

SomeClass myClass;
CCArray pArray();
myClass.foo(pArray); // pretend that there is 1000 objects in pArray

This passes the reference; of pArray into the function, that is, the original pArray into the function. Unfortunately, because it is the original copy, it means that the function foo() can do maliciously evil despicable things like:

void SomeClass::foo( CCArray& rArray ) {
  m_array = array;

  //evil malicious things by adding 1000 more rubbish!
  for ( int i = 0 ; i < 1000; ++i ) {
    m_array->addObject(CCObject::create())
  }
}

SomeClass myClass;
CCArray array();    // pretend that there is 1000 objects in pArray

// now there are 2000 objects instead of 1000 objects in pArray and you are officially heartbroken because you might have wasted 10 hours debugging why.
myClass.foo(pArray);

This actually happens in Java often because Java people are trust each other.
However, C++ programmers do not trust each other so we have a wonderful keyword called const.

With this const keyword, we promise to the users that we will never modify their object:

void SomeClass::foo( const CCArray& array ) {
  m_array = array;

  //COMPILE ERROR!
  for ( int i = 0 ; i < 1000; ++i ) {
    m_array->addObject(CCObject::create());
  }
}

This is why you see argument declarations like "const std::string& str" being used.

But wait, what about pointers?

Pointers work exactly like a value. In other words, there is really no difference between passing by value and passing by pointer IF you treat pointers like an object on it's own (i.e, do not think about its relation to the object so much).

Pointers are simply 4 byte objects that store addresses.

Consider:
void SomeClass::foo( const CCArray *pArray ) {
  // Assume m_array is now "const CCArray * m_array"
  m_array = pArray ;
}
SomeClass myClass;
CCArray array();
myClass.foo(&array);

This simply means that the address of 'array' is copied into 'pArray' and then copied over to 'm_array'. All 3 variables are different variables (they are essentially different) but they hold the same value. Much like if you set i = j = k = 0, all i, j and k are different variables but they all hold 0. It's pretty much the same concept.

Again: Pointers store addresses.

This is not that much different:
void SomeClass::foo( const CCArray *pArray ) {
  // Assume m_array is now "const CCArray * m_array"
  m_array = pArray ;
}
SomeClass myClass;
CCArray * array = CCArray::create(); // now we try to pass by pointer
myClass.foo(array);

Okay read this slowly:

This means that the value held by 'array' is copied into 'pArray' and then copied into 'm_array'. The difference is that the former assigns the address of 'array' into 'pArray', while the latter copies the value held by 'array' into 'pArray'. Both methods have more or less the same results, although what happens is sublimely different.

While there is no deep copying involved in both cases, we must remember that this only means that 'm_array' cannot change the value of the object it is pointing to, but 'array' can. That means that if the object that 'array' is pointing to is modified, the object that 'm_array' is pointing to will be modified too.

After all, even though the pointers are different, they store the same addresses and thus they are pointing to the same object.

Monday, June 9, 2014

[Programming] #4 Finding the smallest bit in a value

More bit magic!

This time we are going to find the smallest bit in a value.

I encountered this problem while implementing uniform grid spatial partition back in school. Basically after some calculations, I would get a resultant value that contains all collisions that need to be resolved encoded in each bit of the value (each bit was representing a part of the world). So I needed a fast way to obtain each and every bit, calculate what I need to calculate, and proceed to the next bit. The problem is getting the bits one by one.

This requires a function that helps me extract the last bit of a value.
If I pass in the number '5' which is 0101, I would want to get 0001.
If I pass in the number '6' which is 0110, I would want to get 0010.

It's pretty nifty because the naive way involves an ugly loop:

int getLastBitNaive( int value ) {
    while ( (value & 1) == 0 ) {
        value = value >> 1;
    }
    return value;
} 
This means for every n bit turned on I would have to loop n times. There are ways to optimize this to make it loop only once of course (by 'remembering' the last position it looped to), but there is a way to do everything without a loop.

Tadah:
int getLastBit( int value ) {
    return value &= ~(value - 1);
}
Basically it's doing an AND operation on it's Two's Complement.

Sadly, returning the positional value (like 0010 = 2, 0100 = 3, etc) still requires either a log2 function or a lookup table. Languages with access to assembly has access to that but that's for another post.

Wednesday, May 28, 2014

[Programming] #3: How NOT to use default arguments

A friend of mine described this code he saw the other day to me in the legacy code of the game he is working in that involves default arguments in functions. I imagine that it looks something like this (it's in PHP):

//PHP!!
bool someFunc( $a = -1, $b = -1 ) {
    // Defensive programming!
    if ( $a == -1 ) {
        return false;
    }

    // More defensive programming!
    if ( $b == -1 ) {
        return false;
    }

    return true;
}
It's funny because you can tell the innocent thought process of the programmer who wrote this. It's simple: preset default unwanted values and reject them if they are not set. That way you can ensure that they are set by the invoker!

Except that it is much more robust and safer if the programmer would just remove the default arguments, together with the defensive programming lines of code.

I wonder if that programmer got stuck while dealing with a language without default argument support?

Tuesday, May 20, 2014

[Programming] #2: Negate bits without conditions

So here's a quick trick with bits! I love bits operations ^_^.

Back when I was a student in Digipen, I did this rather naive way to negate bits:
char bits = 11 //1011
char mask = 2  //0010
if ( bits & mask ) { // AND operator
  bits ^= mask; // XOR operator
} 

Code itself is pretty self explanatory. You will need the 'if' condition because if you XOR 0 and 1, you'll get 1 which is wrong (remember that I wanted to set a 1 to 0). It's a rather shortsighted method that looks only at the bit I want to set to 0, and just...sets it to 0.

There has to be a better way to do this though. Invoking an 'if' condition to do a quick bit-wise operation seems counter-productive. So I sort of wrote them down like so and solve for w, x, y and z.

1011
wxyz &
-----
1001

There are actually 2 answers: 1001 and 1101. From here it just seems kind of mathemagical from my point of view, because the next step in my mind is to somehow find the answer closest to 0010 (closest meaning the least amount of steps to get to 0010), which is 1101. Negating 0010 would just give me 1101.

It's kind of baffling how much sense the answer makes, and how elegant it is since it works for any other values. I don't really know if there is a less accidental approach to derive the answer. If there is let me know. This is how I accidentally derived it (without Google).

Anyway, knowing that we can simply make the adjustment:
char bits = 11 //1011
char mask = 2  //0010
bits &= ~mask; // NEGATE and AND operator, bits = 1001

And we are done!

Tuesday, May 13, 2014

[Programming] #1: Accidental assignment to constants in conditions

Since I'm programming like...ALL THE TIME, might as well share some cool/stupid/retarded/awesome stuff along the way right? 

So let's start with something simple. You should have seen this at least once in your programming career. You want to write a simple statement like so:
bool isHappy = true; // I AM HAPPY!
if ( hello == false ) {   // WHEE~~
  // Awesome stuff happens
}

And you end up writing this:
bool isHappy = true; // I AM HAPPY!
if ( hello = false ) { // WAIT WHAT ARE YOU DOING?!
// Unintentional crap happens
}

Nobody I know does this on purpose, so it's usually an accident of forgetting the extra '=' to turn it into the comparative '==' operator. If you DO write code like this, please don't. For saving 1 extra line (basically placing it outside), you are confusing everyone including and potentially your future self when you revisit the code.

There is a surprisingly elegant way to handle this though. Basically flip the values like so!
bool isHappy = true; // I AM HAPPY!
if ( false = hello ) {   // compile ERROR
  // nothing happens!
}

This will force an error because you are trying to assign the boolean 'hello' to a constant value. If you write it correctly like so:
bool isHappy = true; // I AM HAPPY!
if ( false == hello ) { // YAY!!  
   // Awesome stuff happens
}

It is the same as the original intent! And you won't go wrong this time! Now you can rest assured that all your conditional statements involving constants are safe.