Monday 28 January 2013

Being The Boss.

21-01-13

I've got a module this semester where in, we're to work as small games companies. It's straight forward enough, the class of 30ish students has been split into two teams of ten and we're using the Cry engine and Scrum to create one prototype game each. The game has to be a FPS zombie killing game, multiplayer optional.

Now, in that we're supposed be working as a games company we were all to be given roles based on skills and personal preference. These roles were to be given out by the company producer, an elected executive type role, basically the producer is the guy in charge. This role has real power in the module, they manage the team, make all the final decisions about the game and if necessary, discipline team members. The producer can actually have members of his team kicked off the module if it's deemed necessary.

I'd put my name in for producer. Thinking that, if I got it, it would be good experience relating to my plans after graduation, I also thought it would be nice if I was actually put in charge of a project by my peers instead of just assuming the role.

The campaign was short, we stood in front of the class and presented our fitness for candidacy for 2 minutes each. I'm not well known at the university and I don't even think I'm well liked, I have a habit of winding up total strangers and openly mocking design students, many of whom were on that module. I screwed up my 'speech' delivery pretty royally and made myself look a real idiot. But when the votes came in I (having snuck a peek at the count) was on top... oh... goodie.

So now, here I am leading one of three teams in making a game from the ground up. We're using a commercial grade engine so it's not nearly as much work as it sounds, but there's still an awful lot to do, especially for me. I've got 10 other students relying on me to get a good grade in this module, I've got to manage their time, their tasks and the quality of their work. I've got to organise and lead regular meetings, orchestrate documentation and be held accountable for any and all the problems. I've also got to track it all and report on it. I have a lead artist, designer, tools developer, scripter and 6 team members assigned to various areas. In all I have 2 game designers and 8+ programmers, I'm still waiting for a couple a stragglers to be assigned to my team. All looking to me for decisive guidance and support...

What have I gotten myself in to?

Monday 21 January 2013

My First TDD Kata - Objective C - Xcode 4.5 - OCUnit - ARC

Ok, so I've taken the entire day to work on a programming Kata using TDD.

I started with the same exercise that I was given during an interview for an industrial placement a few weeks ago http://codekata.pragprog.com/2007/01/code_kata_one_s.html just so that I had an idea of where to start.

This code has taken me all day but for good reason.
Firstly I've never used TDD before, nor do I have any idea of how to use OCUnit. I have quite limited experience in programming generally so this was all a bit of a learning curve for me.
Secondly, this paradigm is basically the opposite of the way in which I've learned to program. I find it very, very difficult to think this way.

I'm actually quite pleased with the result. As applications go it's functional, seems robust and the design isn't far off from what I would have designed otherwise. It will be interesting to come back to this Kata later and see what differences there are.

What did I learn?

TDD is a concept, I could hear the criticisms screaming out at me as I did it but once I got going it actually started to come really naturally.

Two problems.
From time to time a tricky change comes up. For example in my application I made the decision to change from scanning in Strings to scanning in item objects. Looking back now I can see how I maybe could have handled this differently and maybe made things a little bit easier on myself. But still there was a lot of time spend pondering this, while outside of TDD the change would have been a no-brainer, just change an argument here and a return type there an done. TDD meant that I wound up writing a whole new function for it to save failing the older tests. I'm hoping that some more practice in TDD will help me to handle this type of situation in the future.

There was a real sense of fragility when programming. When implementing the BOGOF feature I found myself tip-toeing around the code trying to change as little as possible as not to fail my older tests, again I hope that experience will take care of this but I'm not so sure.

The design comes from the refactoring stage really. Refactoring seems to be the time to do a little bit of 'Crystal Balling' a little bit and try to introduce some good code and design practices.

Oh and if you're wondering about the weird way I've worked with NSNumber, well I can't really explain myself, I just had a really hard time with it and the primitives. I may do my next kata in a different language.

My Code.
Tests

 -(void)testCheckOut {  
   NSNumber *expected = [NSNumber numberWithDouble:0.0];  
   NSNumber *result = [scanner checkOut];  
   STAssertEquals([expected doubleValue], [result doubleValue], @"Expected %g, but returned %g", [expected doubleValue], [result doubleValue]);  
 }  
 -(void)testScanItem {  
   NSNumber *expected = [NSNumber numberWithDouble:0.60];  
   [scanner scanItem:(@"apple")];  
   NSNumber *result = [scanner checkOut];  
   STAssertEquals([expected doubleValue], [result doubleValue], @"Expected %g, but returned %g", [expected doubleValue], [result doubleValue]);  
 }  
 -(void)testScanningTwoItems {  
   NSNumber *expected = [NSNumber numberWithDouble:1.20];  
   [scanner scanItem:(@"apple")];  
   [scanner scanItem:(@"apple")];  
   NSNumber *result = [scanner checkOut];  
   STAssertEquals([expected doubleValue], [result doubleValue], @"Expected %g, but returned %g", [expected doubleValue], [result doubleValue]);  
 }  
 -(void)testScanningAnItemWithADifferentPrice{  
   double expected = 0.7;  
   [scanner scanItem:@"banana"];  
   double result = [[scanner checkOut] doubleValue];  
   STAssertEquals(expected, result, @"Expected %g, but returned %g", expected, result);  
 }  
 -(void)testScanningTwoItemsWithDifferentPrices {  
   double expected = 0.7+0.6;  
   [scanner scanItem:@"apple"];  
   [scanner scanItem:@"banana"];  
   double result = [[scanner checkOut] doubleValue];  
   STAssertEquals(expected, result, @"Expected %g, but returned %g", expected, result);  
 }  
 -(void)testScanningMultipleItemsObjectsOfVaryingPrice {  
   Item* apple = [[Item alloc] initWithDescription:@"apple" andPrice:[NSNumber numberWithDouble:0.6]];  
   Item* banana = [[Item alloc] initWithDescription:@"banana" andPrice:[NSNumber numberWithDouble:0.7]];  
   Item* orange = [[Item alloc] initWithDescription:@"orange" andPrice:[NSNumber numberWithDouble:0.5]];  
   double expected = 0.0;  
   [scanner scanItem:apple.description];  
   expected += [apple.price doubleValue];  
   [scanner scanItem:banana.description];  
   expected += [banana.price doubleValue];  
   [scanner scanItem:orange.description];  
   expected += [orange.price doubleValue];  
   [scanner scanItem:apple.description];  
   expected += [apple.price doubleValue];  
   double result = [[scanner checkOut] doubleValue];  
   STAssertEquals(expected, result, @"Expected %g, but returned %g", expected, result);  
 }  
 -(void)testScanningItemAsObject {  
   Item* apple = [[Item alloc] initWithDescription:@"apple" andPrice:[NSNumber numberWithDouble:0.6]];  
   double expected = 0.6;  
   [scanner scanObject:apple];  
   double result = [[scanner checkOut] doubleValue];  
   STAssertEquals(expected, result, @"Expected %g, but returned %g", expected, result);  
 }  
 -(void)testScanningMultipleObjects {  
   Item* apple = [[Item alloc] initWithDescription:@"apple" andPrice:[NSNumber numberWithDouble:0.6]];  
   Item* banana = [[Item alloc] initWithDescription:@"banana" andPrice:[NSNumber numberWithDouble:0.7]];  
   double expected = apple.price.doubleValue + banana.price.doubleValue;  
   [scanner scanObject:apple];  
   [scanner scanObject:banana];  
   double result = [[scanner checkOut] doubleValue];  
   STAssertEquals(expected, result, @"Expected %g, but returned %g", expected, result);  
 }  
 -(void)testBOGOFWithTwoOfTheSameItem {  
   Item* apple = [[Item alloc] initWithDescription:@"apple" andPrice:[NSNumber numberWithDouble:0.6]];  
   double expected = apple.price.doubleValue;  
   [scanner scanObject:apple];  
   [scanner scanObject:apple];  
   double result = [[scanner checkOut] doubleValue];  
   STAssertEquals(expected, result, @"Expected %g, but returned %g", expected, result);  
 }  
 -(void)testBOGOFWithMultipleVaryingItems {  
   Item* apple = [[Item alloc] initWithDescription:@"apple" andPrice:[NSNumber numberWithDouble:0.6]];  
   Item* banana = [[Item alloc] initWithDescription:@"banana" andPrice:[NSNumber numberWithDouble:0.7]];  
   double expected = 0.0;  
   [scanner scanObject:apple];  
   expected += apple.price.doubleValue;  
   [scanner scanObject:apple];  
   [scanner scanObject:banana];  
   expected += banana.price.doubleValue;  
   [scanner scanObject:banana];  
   [scanner scanObject:banana];  
   expected += banana.price.doubleValue;  
   double result = [[scanner checkOut] doubleValue];  
   STAssertEquals(expected, result, @"Expected %g, but returned %g", expected, result);  
 }  

Application

 -(PriceScanner*)init {  
   self.total = [NSNumber numberWithDouble:0.0];  
   self.list = [[NSMutableArray alloc] init];  
   return self;  
 }  
 -(void)scanObject:(Item*)item {  
   [self.list addObject:item];  
 }  
 -(void)scanItem:(NSString*)item {  
   if ([item isEqualToString:@"apple"]){  
     self.total = [NSNumber numberWithDouble:[self.total doubleValue] + 0.6];  
   }  
   if ([item isEqualToString:@"banana"]){  
     self.total = [NSNumber numberWithDouble:[self.total doubleValue] + 0.7];  
   }  
   if ([item isEqualToString:@"orange"]){  
     self.total = [NSNumber numberWithDouble:[self.total doubleValue] + 0.5];  
   }  
 }  
 -(void)applyBOGOF{  
   double newTotal = [self.total doubleValue];  
   NSMutableSet *tempSet = [[NSMutableSet alloc] init];  
   for (Item* i in self.list){  
     if ([tempSet containsObject:i.description]){  
       newTotal -= [i.price doubleValue];  
       [tempSet removeObject:i.description];  
     } else {  
       [tempSet addObject:i.description];  
     }  
   }  
   self.total = [NSNumber numberWithDouble:newTotal];  
 }  
 -(NSNumber*)checkOut {  
   double newTotal = [self.total doubleValue];  
   for (Item* i in self.list){  
     newTotal += i.price.doubleValue;  
   }  
   self.total = [NSNumber numberWithDouble:newTotal];  
   [self applyBOGOF];  
   return self.total;  
 }  

Wednesday 16 January 2013

On Studying

I've been asked a number of times recently on studying. How do you go about studying for an exam, or studying in general? So I figured I'd write a post on it.

1. Time-Keeping

Pace yourself. Students are in the habit of cramming before exams, which is fine as long as it works for you. But even when cramming it's important to keep a steady pace. I'm not a crammer, the thought of leaving studying till the last minute freaks me out and as I have a hard enough time absorbing information it just seems like academic suicide. So I use all the time I have available to review in short bits with a break in between.

Let's say I had 2 weeks to review for a math exam. Each day would schedule 2 lots of 1 hour for a subject, so day 1 I would look at one subject and only one, such as Vectors. After those 2 hours I'd stop regardless of where I was at. Day 2 I would pick another subject and do the same. Each day I pick a new subject and don't repeat the same subject two days in a row. If I wanted to go over vectors again that's fine, just make sure to put something between it, otherwise you end up spending half of the time on one subject.

2. Choose your subjects wisely.
As far as choosing the subject order I start with the things that I'm best at. Ideally you want to cover the course subject matter in its entirety but there's not always time. Because I'm best that them they'll take the least amount of time to review and most importantly, I find the harder things are easier to understand when you have a firm grasp of the easy stuff. And if you get to exam time and you've spent all your time trying to understand only a couple of the very difficult subjects, how prepared are you? Especially if those subjects don't appear on the exam. This can be applied over any time scale, but I suggest that you stick to hour long blocks of study time. I.e. shorten the breaks, not the study.

3. Crowd-sourcing.
I'm against crowd sourcing generally for two reasons; 1. A crowd sourced opinion is no opinion at all. 2. there's a lot of crap out there and some of it is... less correct than it should be. That being said, hit the internet and look at how other people interpret and express the subject. Especially in regards to math and programming I found it really helps.

4. Conversation.
Find someone who understands the subject at about the same level, or another people who is reviewing for the same exam maybe. And converse with them about it. I don't read well, so library books and other literature is largely wasted on me, so I converse, with tutors, with students and some time with my 6 year old son. Any one who will listen and respond to me so that I can assimilate the information spoken out loud.
After explaining this technique to a fellow-student he responded with "So that's why you're always quizzing all us before exams?"

So that's it. That's my studying technique and it's served me pretty well

Tuesday 15 January 2013

Test Driven Development: Prologue.

I'm putting down, for the record, that I'm going to endeavour to do more testing in my software. I plan to do this by means of Test Driven Development. While I plan to do as much work as possible in Xcode probably using GHUnit or OCUnit, visual studio is proving inescapable so I'll probably be using NUnit as well.

What I already know.

I went for an interview for an industrial placement at a software company that uses Extreme Erogramming with TDD for all of their software. Now, I'm a big fan of XP and this has proven hugely successful for this company so I paid attention to their processes.

  1. Write the test. Make sure you give it a name that describes what the test is for.
  2. Fail the test. This was really important, especially as the application starts to grow, because new tests won't always fail, but they should. If you skip this step you can end up wasting a lot of time trying to fix a problem that isn't there.
  3. Get the test to pass as quickly as possible. This is also very important because it stops you spending hours trying to implement the most ideal solution and losing scope of what you're trying to do. Literally just throw in whatever you can to get that test to pass.
  4. Refactor. Make it look nice, now is that time to maybe think about the ideal implementation, but not too much.

This approach gets a lot of criticism from people saying that it promotes bad design, or poorly written code. But the idea behind TDD is that you're focussing on a single problem at a time. So when you start, yeah you have a bunch of really inflexible code and virtually no design in place. But as the number of tests grows and likewise the application, better design falls right into place and you end up with a very robust piece of software.

Anyway, I'll see how I get on. It may be a bit naive of me to just be saying All my products will be TDD from now on but I'm going to give it a go and see how far I get.

Monday 14 January 2013

Group Work

This post may come across as whiny, it's really not meant to be so, I'm just trying to document the entire experience so that the lessons learned are more clear.

This semester I had the pleasure of a group assignment. This particular module awarded a great deal of freedom in the assignment and looked to be very very fun. I was excited about this class.

I was given some advice earlier in the year on group work, that was a few items that are roughly as follows.
1. Find a group of good people, not your friends.
2. You're going to get roped into being the team leader sooner or later.
3. Don't forget that others struggle with this stuff more than you do.

The assignment can be summed up as this; as a group make a game engine and use it to make four different genres of game to demonstrate the engine. Awesome.

Firstly there was the division of work.
We talked and talked about what kind of games we wanted to make in the hopes that it would give us an idea of what types of engine components we needed. 2 weeks of these talks got us no where, so it came down to me to make the decision for everyone.
So I decreed: "I want AI, you do physics, you do graphics."
"But I don't want graphics."
"Fine I'll do graphics. You do physics"
A little time passed and I actually wrote a working tile engine for the game before.
"I can't do physics I want something else, but graphics is too easy."
and so on.

The end resulted in me on physics, him on AI and him on Graphics. But surely the more astute of you have noticed that me, him and him only amount to three, and I mentioned four before.
That's because another 'friend' found himself without a group. So me being the charitable person I am had suggested that we wouldn't have a problem with him joining our group. He was a sharp guy, if a little unreliable, very unreliable... What the hell was I thinking? I regretted this decision the moment I spoke up. Having this guy in our group could only mean disaster...

So then, what should the new guy do? Sound? No too easy. Physics? No too easy (thanks). I want to do a lighting engine. Great, it wasn't a crucial component to the engine and if he finished, it would be a cool effect.

The weeks went on and our graphics were finished in an instant, well done you. We had a tile engine and a screen manger and some basic artwork. Meanwhile I'd created some of the framework of my physics and got a particle system working pretty quickly.
More weeks rolled by and I worked and tuned my physics and started working on collisions and all that goes with it. All was going relatively well, except that guy number 4 vanished. He stopped attending classes, didn't so any work, wouldn't answer messages, nothing. So we counted him as lost and resumed as a three piece. Or so I thought.

Week 10 rolled up, two weeks to go before christmas break, 4 weeks before hand in. I proudly finished my physics engine and started working on my physics based game. The graphics stuff still lay there, unchanged, no sign of AI.
Week 12 came around, it was christmas break and I'd finished my game, I had no more work to do...
First week of christmas break rolled by and there were quite a few premonitions of work but none actually appeared. Second week is when suddenly my team burst into a flurry of activity. Guy number four appeared out of no where with a complete lighting system, which I greedily incorporated into my game, and then he vanished again. The graphics stayed the same and still no sign of AI but I was assured that the work was being done.

3 days before hand in work started to appear, first part of a graphics game and some additional features, then some AI that didn't work, but would eventually. Oh goodie. I was bombarded with questions and problems and ideas, none of which I had any time for but couldn't ignore because my grade was on the line.

I'm happy to report that we made hand in with a working game engine, 4 game demos and a report. It all looks like it was put together by drunken monkeys. I've asked for the project to be graded on an individual basis in hope that I can save some of my marks but we'll have to wait and see. The entire thing was very frustrating for me and while I did my best to be Mr. Optimism and Support I came very close to negotiating a solo contract with the module leader.

In the end our demo received praise, the tutor was impressed by the number of features it engine included and said it was a good project. So alls well that ends well I guess, hopefully we've come away with good marks despite the issues and hopefully we all learned something about working as a team.

What did I learn?
Take advice from academics when it's given to you. They've seen countless students fail and succeed and thus, they're in the best position to advise you on such matters.

Friends usually don't make good co-workers.

As flattering as it is to have people leaning on you for support, it doesn't help you stand up straight.

Never underestimate the amount of work a student can get done in one night.