tag:blogger.com,1999:blog-65627671596071301032024-03-13T12:52:03.543-07:00Kent Anderson - SoftwareKent Andersonhttp://www.blogger.com/profile/14632601949492972207noreply@blogger.comBlogger13125tag:blogger.com,1999:blog-6562767159607130103.post-19304274305493206252016-04-30T18:30:00.001-07:002016-05-01T12:42:28.629-07:00Tip: Use Spotlight Search as a quick calculatorDo you hate waiting for a calculator app to start up on your Mac? Just bring up Spotlight Search on your desktop and type your numbers.<br />
<div>
<br /></div>
<div>
<a href="http://4.bp.blogspot.com/-sgKYQeWs2ww/VyVb7GaMsoI/AAAAAAAABk4/bOfCPSQ_LCgluvdYx00NrsixRCAABrjiACK4B/s1600/Screen%2BShot%2B2016-04-30%2Bat%2B6.25.34%2BPM.png" imageanchor="1"><img border="0" height="401" src="https://4.bp.blogspot.com/-sgKYQeWs2ww/VyVb7GaMsoI/AAAAAAAABk4/bOfCPSQ_LCgluvdYx00NrsixRCAABrjiACK4B/s640/Screen%2BShot%2B2016-04-30%2Bat%2B6.25.34%2BPM.png" width="640" /></a><br />
<br />
<br />
Oh yeah... It works on iOS, too. Happy day!</div>
Kent Andersonhttp://www.blogger.com/profile/14632601949492972207noreply@blogger.com0tag:blogger.com,1999:blog-6562767159607130103.post-24285078842761590872015-08-08T16:52:00.000-07:002015-08-08T16:58:25.260-07:00What I've Learned in Four Months Answering Questions on Stack ExchangeI interview a lot of software developers. I used to ask where they went for answers when they were stuck, but I stopped asking the question because literally <b>everybody</b> answered <a href="http://www.stackoverflow.com/">StackOverflow</a>. I knew it. I go there daily, too.<br />
<br />
StackOverflow is a question and answer forum for programmers. Their format is so effective for getting answers on how to do things, that the users branched out into other topics, and not necessarily techie topics. Though I've been a StackOverflow consumer for years, I signed up and <a href="http://stackoverflow.com/questions/14650232/can-i-use-xcode-as-a-simple-text-editor/28376268#28376268">answered my first question</a> about four months ago (April 2015). Someone asked if it were possible to use the Xcode text editor as a standalone application (without all the IDE stuff). I happened to have been playing around with it like that one day after updating XVim, and happened to know how it could be done. Granted, I had to agree with the others who responded to the question with a <i>"Why on earth would you want to do that?"</i> But I registered as a user and provided an answer to the question. It was an old question from 2013, obviously long-forgotten by the original poster ("OP" in online forum speak), and nobody has up-voted my answer. It's all good. I have 127 reputation points ("rep") on StackOverflow. It's not impressive. Basically, every question I can think to ask, already has an answer, and you don't usually get up-votes for saying something that's already been said.<br />
<br />
There is a footer on each StackOverflow page with links to other forums in the StackExchange network. I hadn't bothered to notice any of those in the years I've been a user, though many of them looked familiar, as I had been sent to them by some of my Google searches. I remember thinking when I would see some of those other sites, "Hey, they must use the same forum software as StackOverflow." Silly me. Now I know.<br />
<br />
But since I had already signed up for StackOverflow, I figured I'd check out some of their other networks. Since I spent five years aspiring to be a successful financial advisor, I immediately took an interest in the <a href="http://money.stackexchange.com/">Finance and Money</a> site. I found I could answer a lot of folks' questions, and in looking at some of the other answers, I felt like my conservative approach to money and investing might be appreciated. In four months, I've built up about 2000 rep, which is respectable.<br />
<br />
Eventually I ended up spending most of my time in <a href="https://workplace.stackexchange.com/">The Workplace</a> network. This site deals with dilemmas, problems, and pitfalls in the workplace. I've really enjoyed offering advice on how I would approach a situation, and reading about others' advice for the same situations. I've learned a lot from people around the world. As of August 2015, I've built up over <a href="https://workplace.stackexchange.com/users/33312/kent-anderson">6000 rep in The Workplace</a>, which puts me in the top 0.66% of rep growth for the year. Not bragging, though, as there are others who are growing faster than I am, and they deserve it, too -- they give good answers, and generally avoid making someone feel bad for having asked a question.<br />
<br />
I've enjoyed spending time on StackExchange. It has filled my need to write, which explains the long gap in this blog (which is something I intend to remedy). I've learned a thing or two about how to be successful in an online discussion/Q&A forum. I thought I'd share.<br />
<br />
<h2>
#1: Be Nice</h2>
I started joining into discussions on <a href="https://www.reddit.com/">Reddit</a> just before getting into StackExchange. When it was appropriate, I'd post a link to a blog post or one of my iOS apps, just to try to generate some traffic to my site. Reddit is similar in many ways to StackExchange, with multiple "sub-reddits," or groups, organized along topics of interest. However, Reddit is more of a discussion forum, whereas StackExchange is a Q&A forum. In those discussions, Redditors (users of Reddit) can often become very rude and crude. To quote the great literary figure, Eliza Doolittle (My Fair Lady), "I'm a good [boy], I am." I've lost most of my interest in Reddit, simply over this one point. F-bombs are not the only way to express strong feelings.<br />
<br />
The top rule in each StackExchange site is Be Nice. (See the Help section of each forum: <a href="https://workplace.stackexchange.com/help">The Workplace</a>, for example). And they enforce it. I actually appreciate that profanity and vulgarity are frowned upon. It elevates the conversation, and keeps the content focused on the questions and answers. I've noticed that many users who join the forum and come on too strong, either soften their tones, or drift away from the site.<br />
<br />
When I signed up for StackExchange, I decided to use my real name as a way to encourage myself to always say things I would be willing to have attributed to me later. It has worked well, so far. There have been times where I've been tempted to send out a little extra "Internet Snark" and have decided against it.<br />
<br />
<h2>
The World Is a Huge Place</h2>
<div>
StackExchange is used world wide. Although the official language of the site is English, it is used by people from almost every continent (I can't confirm that I've seen anything from Antarctica). I am amazed at how common our questions are, and at the same time, how diverse our cultures are. In The Workplace, it is very common for responders to ask for clarification about which country the OP (original poster) is in. The answers that will follow will be very different when it is known that someone is working in London as opposed to New York City. Labor laws are vastly different. And if you ask me, based on the headaches our European and Asian friends have, I am perfectly happy to stay in the USA and work. But I appreciate seeing the different ways people view the same issues. </div>
<div>
<br /></div>
<h2>
Not Everyone Will Agree With You</h2>
<div>
...and that's ok. It happens to everyone who contributes regularly to such a forum. You spend a few minutes crafting the best answer you've ever written. Your advice is sound. Your spelling is checked. You hit the Submit button, and within five minutes, you have two down-votes, and a comment telling you why your advice is bad. (It's usually said in a nice way, but it's still clear that they disagree with you.)</div>
<div>
<br /></div>
<div>
It's ok to have the Internet disagree with you. You have a choice: blow it off and disregard it, or look at it and see if maybe you can learn something from it. I've done both. </div>
<div>
<br /></div>
<h2>
A Final Tip: Be the First to Answer ;)</h2>
<div>
Ok, this is unabashed self-aggrandizement, but if you want to build your rep, you get a boost from being the first answer (as long as it's a good answer). Most good questions get multiple good answers. Most readers who vote on answers (about 10% of viewers, by my observations) vote for the first one they come across and agree with. Answers are ordered according to the number of votes they receive, so when a question has just been asked, the first answer is often the one that gets the most votes, and would then be ranked at the top of the page, thus helping it to get even more votes.</div>
<div>
<br /></div>
<div>
I recently tried an experiment. I <a href="https://workplace.stackexchange.com/questions/50642/my-ex-employer-is-sending-emails-to-customers-in-my-name/50645#50645">answered a question</a> (first, mind you) and it got 96 up-votes. If it had gotten to 100, I'd have earned a gold badge for a Great Answer. Those are fun to get. It's always nice to have strangers from around the planet validate your self-worth. I was left wondering whether four more kind souls would come along and agree with me. </div>
<div>
<br /></div>
<div>
But then, I remembered seeing another user (who has more than 88,000 rep) update his own answers from a couple of years ago. Anytime a question or answer is updated, it shows up at the top of the Active Questions list (the default page when you arrive at the site). So it gets an old question in front of new users. I have no reason to believe he was trying to boost his rep. From what I've observed, he values refactoring his answers (to borrow a programming term - it means fixing things now that we've learned a thing or two). But I'm not above juicing my rep a little. So I made an update to the question with the intent that it would get listed in the recent update list, which it did. Now, my update was legitimate. I incorporated advice that was provided by others as comments to my answer. It improved my answer, and if StackExchange had a way to share rep with other users whose ideas make your answer better, I would do it. But it worked! I got my four more votes, hit 100, and got my fourth gold badge. I feel fulfilled.</div>
<div>
<br /></div>
<h2>
PS...</h2>
<div>
You know the funny thing about me is this... I get a little obsessive about checking whether anyone agrees with what I've said. I'd post an answer and then check 100 times throughout the rest of the day to see if anyone voted for it, or commented, or responded in any way. I'm a little pathetic that way. After publishing this post, I have the strongest urge to go check my stats and see whether I'm getting any extra page hits on this blog. Sigh. I need a life.</div>
Kent Andersonhttp://www.blogger.com/profile/14632601949492972207noreply@blogger.com0tag:blogger.com,1999:blog-6562767159607130103.post-159399193824600162015-03-30T10:41:00.001-07:002015-03-30T10:47:55.840-07:00Kibana Quick-Tip: Save those Dashboards!I like to learn things the hard way, I guess. In an attempt to keep my installation clean, I went to remove the <b>kibana-int</b> index from my system. Why? Because I didn't know what it was, and it had a date from way back when I originally set up my ELK stack on this system. <i>(No, I don't actually read docs. I apparently must always learn by sad experience. Sigh.) </i>It was easy enough to delete, and I felt good that I had cleaned up some cruft.<br />
<div>
<br /></div>
<div>
Except, now my dashboards are gone. Turns out, Kibana creates its own index, named <b>kibana-int</b> for storing the dashboards and stuff. Oops. </div>
<div>
<br /></div>
<div>
Did I have a backup of my indices? No - it's just log messages, and the originals are still on their respective systems. We use ELK as a convenience when things are amiss, which rarely happens (except when I go around "cleaning" things!). So I had to spend an hour rebuilding my dashboards. The new one is actually better than the old one, so I suppose that's the optimistic view of the story.</div>
<div>
<br /></div>
<div>
<b>Here's the tip:</b> Export your dashboards to a local file, and keep them under source control, like git.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-zS9EJKhTb6A/VRmIJS7wWJI/AAAAAAAAANU/ZCM7zf1lUnY/s1600/Kibana_Dashboard_Export.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-zS9EJKhTb6A/VRmIJS7wWJI/AAAAAAAAANU/ZCM7zf1lUnY/s1600/Kibana_Dashboard_Export.png" height="132" width="400" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Then, if you ever need to reload your dashboard after a disaster (or a "cleaning"), you can just load it from a local file in the Open Dashboard menu. <b>Bonus:</b> You can also open a dashboard from a Gist using the same Advanced screen under the Open icon.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-dficVeQdkjM/VRmJzUMdDNI/AAAAAAAAANg/gDJZ65fwjG0/s1600/Kibana_Dashboard_Import.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-dficVeQdkjM/VRmJzUMdDNI/AAAAAAAAANg/gDJZ65fwjG0/s1600/Kibana_Dashboard_Import.png" height="255" width="400" /></a></div>
<div>
<br /></div>
Kent Andersonhttp://www.blogger.com/profile/14632601949492972207noreply@blogger.com0tag:blogger.com,1999:blog-6562767159607130103.post-42284252445821828002015-02-25T07:08:00.000-07:002015-02-25T09:22:36.906-07:00How I Made It: TreeDudes Launch iOS Game<b>The Game:</b> Arrange various tools in Rube Goldberg fashion to help the character land on a target after being flung from the launch platform. <i>(<a href="https://itunes.apple.com/us/app/treedudes-launch/id788877889?ls=1&mt=8">See it in iTunes</a>)</i><br />
<br />
<span style="font-size: large;">Tools</span><br />
<br />
<b><a href="http://www.cocos2d.org/">Cocos2d-iPhone</a></b>: This is a great library. I would have used SpriteKit but it didn't exist when I started this project.<br />
<br />
<b><a href="https://chipmunk-physics.net/">Chipmunk Physics:</a></b> I bought the license so I could have the Objective-C library. Was it worth the $250? Yes. I believe it was. I didn't have to waste time mentally "bridging" between Obj-C and C. It's a high quality library.<br />
<br />
<b><a href="http://www.indeeo.com/idraw/">iDraw:</a></b> Vector drawing software. All artwork is drawn in iDraw. I started with <b><a href="https://inkscape.org/en/">InkScape</a></b>, but I found iDraw was easier to use and the file format was easier to read. What? What could be easier than SVG? Well, I generated my physics environment by drawing it as a layer in the file, then parsing it and writing Objective-C to build the Chipmunk physics objects at runtime. InkScape uses a lot of transforms, and reverse engineering those got really hard. iDraw uses a zipped plist as a file format, which made it really easy to crack it open and see what it was doing. Its internals are very easy to read and understand, and made my code generator a lot easier to write. It was definitely worth the $25 on the App Store.<br />
<br />
<b><a href="http://audacity.sourceforge.net/">Audacity</a>:</b> Sound effects were recorded using things around the house (like a finger in the cheek for the balloon popping sound), recorded on an iPod Touch. I used Audacity to filter out the background noise and tweak the pitches and things to get the right sound.<br />
<br />
<b><a href="https://itunes.apple.com/us/app/garageband/id682658836?mt=12">GarageBand</a>:</b> My wife came up with the main menu audio loop, which I believe is just a pre-canned Apple Loop with a bit of a rhythm added in. She used the iPad version.<br />
<br />
<b><a href="http://www.bmglyph.com/">bmGlyph</a>:</b> Used for creating bitmapped fonts. I started by doing all my text as graphics in iDraw, but when I went add Spanish localization, I had to switch to text. It was also worth the $10 on the App Store. And the developer is very responsive. I e-mailed him with a couple of questions and a problem, and he asked me to send a sample file so he could see what I was seeing. He sent back some suggestions to accomplish what I was trying to do.<br />
<br />
<b><a href="https://www.codeandweb.com/texturepacker">TexturePacker</a>:</b> Used to create sprite sheets to optimize my graphics footprint (a little).<br />
<br />
<b><a href="http://www.graphicsmagick.org/">GraphicsMagick</a>:</b> Command line tool used to crop images and prepare them for loading into sprite sheets.<br />
<br />
<b><a href="https://developer.apple.com/xcode/">Xcode</a>:</b> This is the de facto for Apple development, of course. I am a Vi-guy, so I used the <a href="https://github.com/XVimProject/XVim"><b>Xvim</b></a> plugin so I could feel a little more at home.<br />
<br />
<b>iOS Simulator:</b> I used this mainly to debug the In-App purchases and the GameCenter interaction. It was very difficult debugging the gameplay, since the Cocos2d and Chipmunk animations were run by the simulator and were very slow.<br />
<br />
<span style="font-size: large;">Languages</span><br />
<br />
<b>Objective-C:</b> All of the game is written in Obj-C. This was my first exposure to it and after the learning curve, I would prefer Obj-C over C++ for almost any development effort. (Note: I am excited by <a href="https://developer.apple.com/swift/">Swift</a>, and am using it on a current project that is just getting underway.)<br />
<br />
<b>Python:</b> My supporting scripts are written in Python. <span style="font-family: Courier New, Courier, monospace;">idraw_to_gamelevel</span> is the script that cracks open the iDraw files and generates the Objective-C classes that implement each level. <span style="font-family: Courier New, Courier, monospace;">export</span> is the script that took the manually exported png and 2x png output from iDraw, cropped out individual images, created sprite sheets, and copied them over to the right place in the Xcode project. I decided to export the 2x and 1x images separately from iDraw. I used GraphicsMagick to crop, but the quality of the scaled files (downscaled from 2x to 1x) wasn't as good.<br />
<br />
<span style="font-size: large;">Workflow</span><br />
<br />
Once I finally zeroed in on the game concept, it took about year and a half of mornings, evenings, weekends, and burning out a few times, to get it ready to submit to the App Store. There was a lot of learning I had to do, and I'm convinced I did a lot of things wrong. There is some major technical debt rolled into this app that I will definitely have to refactor if I do any more work on it.<br />
<br />
I started by getting a basic GameLayer class in place. This is the class that sets up the level (menus, scenery, etc) and then is called by Cocos2d each frame. It would call Chipmunk to calculate all the physics. It also implements all the Chipmunk callbacks for collision handling. It was basic at first, then evolved into much more and took on a lot of technical debt (i.e. things I should have done better, but didn't) because I wasn't disciplined enough to refactor as I went. I just got into heads-down mode and made stuff work.<br />
<br />
With the GameLayer in place, I could start adding levels. To create a level, I would draw it out in iDraw, then add the physics layer. The physics layer is just line segments and shapes with specific attributes that would point to more details in a JSON file. The code generator would load both files at the same time and use the attributes to generate line segments and shapes with certain collision behaviors in an Obj-C class that is loaded by the GameLayer for each level (see below for an example).<br />
<br />
The rest is just the grinding work of producing artwork (I am not an artist, but would someday love to take the time to develop that skill), and adding functionality to the GameLayer to support new things I wanted to add to the game.<br />
<br />
<span style="font-size: large;">Would I do it differently if I started over?</span><br />
<br />
Yes, I would. While Cocos2d and Chipmunk are fantastic libraries, the workflow around SpriteKit is just so much easier. In order to support Android, I have considered whether this game would work well as a hybrid web app using Cocos2d and Chipmunk Javascript libraries on top of the Apache Cordova framework. It's not a complex game, but some of the levels have a couple thousand line segments and other physics objects. The ChipmunkJS demo app for the Logo Smash, which also has a couple thousand objects, is pretty slow.<br />
<br />
<span style="font-size: large;">Have I made any money?</span><br />
<br />
A little. $40, roughly. About half in early sales to friends and family, and the rest in ads after making it a free download with In-App purchase. $0 from In-App purchases, by the way. I'm not sad about it. <a href="http://kent-anderson-software.blogspot.com/2015/01/paying-indie-dues-and-simplifying-my.html">I wrote about it recently</a>.<br />
<br />
<span style="font-size: large;">Screenshots</span><br />
<br />
Here are a few screenshots of my Physics generation...<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-C6NwjR86Wi0/VO3QJLC9iqI/AAAAAAAAAH4/5LkOqH0sGaA/s1600/Level1-iDraw.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="Level 1" border="0" src="http://3.bp.blogspot.com/-C6NwjR86Wi0/VO3QJLC9iqI/AAAAAAAAAH4/5LkOqH0sGaA/s1600/Level1-iDraw.png" height="382" title="Level 1" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Level 1 Scene</td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-Ygxv9d_dKNo/VO3QI-tbWuI/AAAAAAAAAH0/WNVDO2PU5kg/s1600/Level1-Physics-iDraw.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="Level 1 With Physics" border="0" src="http://4.bp.blogspot.com/-Ygxv9d_dKNo/VO3QI-tbWuI/AAAAAAAAAH0/WNVDO2PU5kg/s1600/Level1-Physics-iDraw.png" height="379" title="Level 1 With Physics" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Level 1 Scene with Physics Layer Visible</td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-92WJHSMq3tw/VO3Qrk2PxiI/AAAAAAAAAIE/4RjU96GUbIQ/s1600/Level5-Physics-iDraw.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img alt="Level 5 Physics" border="0" src="http://2.bp.blogspot.com/-92WJHSMq3tw/VO3Qrk2PxiI/AAAAAAAAAIE/4RjU96GUbIQ/s1600/Level5-Physics-iDraw.png" height="382" title="Level 5 Physics" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Level 5 Physics Layer</td></tr>
</tbody></table>
<br />
And finally, here is the code generated for Level 1.<br />
<br />
Level1.h
<br />
<pre class="brush:cpp">// AUTO-GENERATED: LEVEL 1
#import "GameLevel.h"
@interface GameLevel_1 : GameLevel
@end
</pre>
<br />
Level1.m
<br />
<pre class="brush:cpp">// AUTO-GENERATED: LEVEL 1
#import "Level1.h"
#import "Character.h"
#import "FieldObject.h"
#import "CollisionTypes.h"
#import "Screen.h"
#import "Animation.h"
#define POSSIBLY_UNUSED(A) if (A)
@implementation GameLevel_1
- (bool) debug { return NO; }
- (id) init {
if ((self = [super init])){
self.ID = 1;
self.gameSize = CGSizeMake(SCRNX(2048),SCRNY(1024));
//Gems/Balloons
[self.gems addObject:[GemDefinition gem:0 at:ccp(SCRNX(936.102388),SCRNY(599.268964))]];
[self.gems addObject:[GemDefinition gem:0 at:ccp(SCRNX(1015.272542),SCRNY(599.268964))]];
[self.gems addObject:[GemDefinition gem:0 at:ccp(SCRNX(1094.442735),SCRNY(599.268964))]];
[self.gems addObject:[GemDefinition gem:0 at:ccp(SCRNX(1051.625752),SCRNY(679.538748))]];
[self.gems addObject:[GemDefinition gem:1 at:ccp(SCRNX(973.155487),SCRNY(679.538748))]];
[self.gems addObject:[GemDefinition gem:2 at:ccp(SCRNX(1049.410717),SCRNY(837.354861))]];
[self.gems addObject:[GemDefinition gem:0 at:ccp(SCRNX(1173.612969),SCRNY(599.268964))]];
[self.gems addObject:[GemDefinition gem:1 at:ccp(SCRNX(1130.096015),SCRNY(679.538748))]];
[self.gems addObject:[GemDefinition gem:1 at:ccp(SCRNX(1007.524312),SCRNY(759.808502))]];
[self.gems addObject:[GemDefinition gem:1 at:ccp(SCRNX(1096.744143),SCRNY(759.808483))]];
}
return self;
}
- (ChipmunkSpace*) createSpace {
ChipmunkSpace *space = [[ChipmunkSpace alloc] init];
space.gravity = cpv(0.000000,-400.000000);
space.damping = 0.900000;
ChipmunkSegmentShape *wall; POSSIBLY_UNUSED(wall);
//LaunchPad
self.launchPad = [[ChipmunkSegmentShape alloc] initWithBody:[space staticBody] from:cpv(SCRNX(-2.000000),SCRNY(597.000000)) to:cpv(SCRNX(230.000000),SCRNY(597.000000)) radius:0];
self.launchPad.elasticity = 0;
self.launchPad.friction = 0;
self.launchForce = cpv(SCRNX(15.000000),SCRNY(0.000000));
self.launchPad.collisionType = CT_LaunchPadSegment;
[space add:self.launchPad];
self.launchSensor = [[ChipmunkSegmentShape alloc] initWithBody:[space staticBody] from:cpv(SCRNX(-2.000000),SCRNY(597.000000)) to:cpv(SCRNX(230.000000),SCRNY(597.000000)) radius:0];
self.launchSensor.sensor = YES;
self.launchSensor.collisionType = CT_LaunchPad;
[space add:self.launchSensor];
//Target(s)
self.target = [[ChipmunkSegmentShape alloc] initWithBody:[space staticBody] from:cpv(SCRNX(1815.437917),SCRNY(121.097526)) to:cpv(SCRNX(1915.267717),SCRNY(121.097526)) radius:2];
self.target.elasticity = 0;
self.target.friction = 2.5;
self.target.collisionType = CT_Target;
[space add:self.target];
//Obstacles
Sensor *sensor; POSSIBLY_UNUSED(sensor);
ActiveFieldObject *afo; POSSIBLY_UNUSED(afo);
RotationAnimation *rotation; POSSIBLY_UNUSED(rotation);
OscillationAnimation *oscillation; POSSIBLY_UNUSED(oscillation);
FollowPathAnimation *followPath; POSSIBLY_UNUSED(followPath);
wall = [[ChipmunkSegmentShape alloc] initWithBody:[space staticBody] from:cpv(SCRNX(223.266024),SCRNY(584.583267)) to:cpv(SCRNX(209.997305),SCRNY(509.934742)) radius:4.000000];
[wall setPrevNeighbor:cpv(SCRNX(223.266024),SCRNY(584.583267)) nextNeighbor:cpv(SCRNX(209.997305),SCRNY(509.934742))];
wall.elasticity = 0.000000;
wall.friction = 1.000000;
[space add:wall];
wall = [[ChipmunkSegmentShape alloc] initWithBody:[space staticBody] from:cpv(SCRNX(209.997305),SCRNY(509.934742)) to:cpv(SCRNX(200.227196),SCRNY(414.490535)) radius:4.000000];
[wall setPrevNeighbor:cpv(SCRNX(209.997305),SCRNY(509.934742)) nextNeighbor:cpv(SCRNX(200.227196),SCRNY(414.490535))];
wall.elasticity = 0.000000;
wall.friction = 1.000000;
[space add:wall];
wall = [[ChipmunkSegmentShape alloc] initWithBody:[space staticBody] from:cpv(SCRNX(200.227196),SCRNY(414.490535)) to:cpv(SCRNX(192.135846),SCRNY(195.342987)) radius:4.000000];
[wall setPrevNeighbor:cpv(SCRNX(200.227196),SCRNY(414.490535)) nextNeighbor:cpv(SCRNX(192.135846),SCRNY(195.342987))];
wall.elasticity = 0.000000;
wall.friction = 1.000000;
[space add:wall];
wall = [[ChipmunkSegmentShape alloc] initWithBody:[space staticBody] from:cpv(SCRNX(192.135846),SCRNY(195.342987)) to:cpv(SCRNX(186.883136),SCRNY(34.529300)) radius:4.000000];
[wall setPrevNeighbor:cpv(SCRNX(192.135846),SCRNY(195.342987)) nextNeighbor:cpv(SCRNX(186.883136),SCRNY(34.529300))];
wall.elasticity = 0.000000;
wall.friction = 1.000000;
[space add:wall];
wall = [[ChipmunkSegmentShape alloc] initWithBody:[space staticBody] from:cpv(SCRNX(1798.693016),SCRNY(38.837067)) to:cpv(SCRNX(1816.270816),SCRNY(93.071957)) radius:4.000000];
[wall setPrevNeighbor:cpv(SCRNX(1798.693016),SCRNY(38.837067)) nextNeighbor:cpv(SCRNX(1816.270816),SCRNY(93.071957))];
wall.elasticity = 0.000000;
wall.friction = 1.000000;
[space add:wall];
wall = [[ChipmunkSegmentShape alloc] initWithBody:[space staticBody] from:cpv(SCRNX(1816.270816),SCRNY(93.071957)) to:cpv(SCRNX(1817.306316),SCRNY(119.317667)) radius:4.000000];
[wall setPrevNeighbor:cpv(SCRNX(1816.270816),SCRNY(93.071957)) nextNeighbor:cpv(SCRNX(1817.306316),SCRNY(119.317667))];
wall.elasticity = 0.000000;
wall.friction = 1.000000;
[space add:wall];
wall = [[ChipmunkSegmentShape alloc] initWithBody:[space staticBody] from:cpv(SCRNX(1931.003741),SCRNY(37.626398)) to:cpv(SCRNX(1918.337241),SCRNY(61.221718)) radius:4.000000];
[wall setPrevNeighbor:cpv(SCRNX(1931.003741),SCRNY(37.626398)) nextNeighbor:cpv(SCRNX(1918.337241),SCRNY(61.221718))];
wall.elasticity = 0.000000;
wall.friction = 1.000000;
[space add:wall];
wall = [[ChipmunkSegmentShape alloc] initWithBody:[space staticBody] from:cpv(SCRNX(1918.337241),SCRNY(61.221718)) to:cpv(SCRNX(1913.339041),SCRNY(120.294938)) radius:4.000000];
[wall setPrevNeighbor:cpv(SCRNX(1918.337241),SCRNY(61.221718)) nextNeighbor:cpv(SCRNX(1913.339041),SCRNY(120.294938))];
wall.elasticity = 0.000000;
wall.friction = 1.000000;
[space add:wall];
wall = [[ChipmunkSegmentShape alloc] initWithBody:[space staticBody] from:cpv(SCRNX(-2.723418),SCRNY(46.361666)) to:cpv(SCRNX(2497.276587),SCRNY(46.361666)) radius:4.000000];
[wall setPrevNeighbor:cpv(SCRNX(-2.723418),SCRNY(46.361666)) nextNeighbor:cpv(SCRNX(2497.276587),SCRNY(46.361666))];
wall.elasticity = 0.000000;
wall.friction = 1.000000;
[space add:wall];
//Gems
for (GemDefinition* gem in self.gems){
ChipmunkBody *body = [ChipmunkBody staticBody];
body.pos = gem.position;
ChipmunkShape *shape;
int size = 40 - (5 * gem.type);
shape = [ChipmunkPolyShape boxWithBody:body width:SCRNX(size) height:SCRNY(size)];
shape.sensor = YES;
shape.collisionType = CT_Gem;
gem.shape = shape;
[space add:shape];
}
//ActiveFieldObjects
for (ActiveFieldObject *obj in self.gameConfig.activeFieldObjects){
obj.body = [obj.definition createInSpace:space position:obj.position angle:obj.settings.rotation rogue:NO];
}
return space;
} // end of createSpace()
- (void) addSceneryToNode:(CCNode*)node staticNode:(CCNode*)staticNode {
CCSprite *image; POSSIBLY_UNUSED(image);
image = [self loadImage:@"L1-Bkg-0-0.png"];
image.anchorPoint = ccp(0,0);
image.position = ccp(SCRNX(0.000000),SCRNY(0.000000));
[node addChild:image z:-999999];
image = [self loadImage:@"L1-Bkg-1024-0.png"];
image.anchorPoint = ccp(0,0);
image.position = ccp(SCRNX(1024.000000),SCRNY(0.000000));
[node addChild:image z:-999999];
}
@end
</pre>
Kent Andersonhttp://www.blogger.com/profile/14632601949492972207noreply@blogger.com0tag:blogger.com,1999:blog-6562767159607130103.post-27209832863785644922015-02-20T07:03:00.004-07:002015-02-20T08:03:41.045-07:00Should I Still Be Using Vim in 2015? (Yes)Last summer, I <a href="http://kent-anderson-software.blogspot.com/2014/06/why-i-can-stop-using-vim.html">wrote a post</a> about why I keep looking for something better than Vim, and why I keep coming back to it when it's time to actually sit down and get some work done. I've been editing the Vi way since 1992, but there are some things about Vim that I wish were better (easier to script, prettier to look at, better context-aware completion). So I occasionally wander away from Vim to see if things are actually better. The past few months have included the longest such wandering episode I've had in years. There are newer options for text editors out there, and they have passionate followers. Here's what I've seen on my little text editor excursion (in no particular order).<br />
<br />
<span style="font-size: large;">Atom</span><br />
<a href="http://www.atom.io/">http://www.atom.io</a><br />
<br />
Github's text editor was released as open source last summer after an invitation-only beta. It's off to an impressive start with a great community around it.<br />
<br />
<b>What did I like about it? </b><br />
<br />
<ul>
<li>Cross-platform. I used it on Mac and Windows (performs a little less smoothly on Windows).</li>
<li>Autocomplete2 plug-in is pretty good (though it was very slow looking up C completions, presumably calling out to clang).</li>
<li>Package manager built-in. </li>
<li>Extremely easy to find and set configuration options via the settings pane search field.</li>
</ul>
<br />
<b>Why didn't I stick with it?</b> It has a huge memory footprint at runtime, and spawns at least two processes just to edit a single file. It gets sluggish if you try to open very large text files. Built first on webkit, then on Chrome, it is a platform suited for displaying rich content, but it does so at great expense.<br />
<br />
<span style="font-size: large;">Brackets</span><br />
<a href="http://www.brackets.io/">http://www.brackets.io</a><br />
<br />
Adobe's text editor geared toward web developers has been around for a few years. It's community is less productive than Atom's. But the editor is solid.<br />
<br />
<b>What did I like about it?</b><br />
<br />
<ul>
<li>Cross-platform.</li>
<li>Inline editors for things like CSS options and colors.</li>
<li>Live preview</li>
</ul>
<div>
<b><br /></b>
<b>Why didn't I stick with it?</b> It does a great job in web development, but is just a regular editor for other types of projects. It is built on a similar platform as Atom (webkit or Chrome). I criticize it the the same as Atom - huge resources are required to edit a file. </div>
<div>
<br /></div>
<div>
<span style="font-size: large;">LightTable</span></div>
<div>
<a href="http://lighttable.com/">http://lighttable.com</a></div>
<div>
<br /></div>
<div>
This is a very interesting project. It aims to be a platform for evolving how we develop software. The problem is it's really slow. Really slow. And unlike Atom, which is highly usable right out of the box, the cool features shown on the website require configuration to achieve, and the docs weren't helpful, and it just wasn't worth it. </div>
<div>
<br /></div>
<div>
<span style="font-size: large;">Sublime Text</span></div>
<div>
<a href="http://www.sublimetext.com/">http://www.sublimetext.com</a></div>
<div>
<br /></div>
<div>
Loads of people love this editor. I just don't. It didn't impress me that it could offer anything I didn't already have available to me in TextMate. Being a paid product, I couldn't bring myself to pay for it (which I believe you should, if you use it) when it didn't provide a clear advantage over other options I have that don't cost anything.</div>
<div>
<br /></div>
<div>
<span style="font-size: large;">TextMate2</span></div>
<div>
<a href="http://macromates.com/">http://macromates.com</a></div>
<div>
<br /></div>
<div>
This venerable editor for Mac is fast, lightweight and has a loyal following. The fan base has waned, though, as the long-awaited TextMate2 took too long to come about. In reading posts and forums, it seems like most folks that bailed went toward SublimeText. But TextMate2 is now available as free software. </div>
<div>
<br /></div>
<div>
<b>What did I like about it?</b></div>
<div>
<ul>
<li>You can tell its author(s) understand Unix workflows in the way bundles (plug-ins) work. It's a comfortable environment for me. </li>
<li>It's fast, and lightweight.</li>
<li>Once you become familiar with the keyboard shortcuts, you can be very efficient navigating and editing code and text in general.</li>
<li>Rmate is nice - it's a ruby script you can install on remote machines you access that allows you to easily edit a remote file in TextMate on your local machine. Works great.</li>
</ul>
<div>
<b><br /></b>
<b>Why didn't I stick with it?</b> At my day job, I have to work in a Windows environment very often, and it's not cross-platform. Also, although you can get very efficient at editing text, the keyboard shortcuts don't seem to have any rhyme or reason to the composition. I may be getting old, but I just couldn't remember the (seemingly) random key combinations to do things. I always had to have a cheat sheet handy.</div>
</div>
<div>
<br /></div>
<div>
<span style="font-size: large;">Emacs</span></div>
<div>
<a href="https://www.gnu.org/software/emacs/">https://www.gnu.org/software/emacs/</a></div>
<div>
<a href="http://emacsformacosx.com/">http://emacsformacosx.com</a></div>
<div>
<a href="http://aquamacs.org/">http://aquamacs.org</a></div>
<div>
<br /></div>
<div>
Yes, no editor walkabout would be complete without at least attempting (once again) to like Emacs. I just don't. Maybe it's the learning curve? Maybe I'm just really picky? I ended up using Aquamacs this time around, and was impressed by it. They managed to put a glossy finish on a stodgy old editor and have pulled it off really well. Their default settings make it more of a good Mac citizen, which I appreciated. But, in the end, I just didn't enjoy the learning process (same as always). One thing I liked about Emacs this time around, though, was the ability to sudo while editing a remote file via Tramp. I wish Vim's netrw could do that.</div>
<div>
<br /></div>
<div>
<span style="font-size: large;">The Return to Vim</span></div>
<div>
<br /></div>
<div>
Anyone reading this article might infer that I'm just itching to leave Vim as soon as I find a viable alternative. There is some truth to that. I think everybody wants to improve their lives if they can. But anything that is going to aspire to replace Vim in my toolbox is going to have to overcome more than 20 years of familiarity with the Vi way of navigating text. </div>
<div>
<br /></div>
<div>
For me, there is just no more efficient way to navigate and manipulate text, especially source code, than the Vi way. </div>
<div>
<br /></div>
<div>
<i>Vim is cross platform in a way that no other cross platform editor can be</i>, except maybe Emacs, but the key chords are too burdensome (yes I'm that petty), even after remapping my CapsLock and Return keys. The problem all other editors have between Mac and Windows is the difference between Cmd and Ctrl. Yep - on Mac it's Cmd-S to save. On Windows it's Ctrl-S. Using an Apple keyboard, this is a big deal, because muscle memory causes me to hit Cmd-S, which on a PC keyboard usually ends up being Alt-S, and in VirtualBox, is Win-S. See my dilemma? In Gvim, or MacVim, I can use the common keystrokes if I want to, but I don't have to. Instead, I can use <span style="font-family: Courier New, Courier, monospace;">:w</span>, which I don't even have to think about. I think 'Save' and my fingers have typed <span style="font-family: Courier New, Courier, monospace;">:w</span> within milliseconds. Even in Emacs, after remembering what it is, I'd still have to hit <span style="font-family: Courier New, Courier, monospace;">Ctrl-x, Ctrl-s</span>. That's just too much :)</div>
<div>
<br /></div>
<div>
No other editor provides the notion of Text Objects. That is one of Vim's secret killer features. Having the ability to address a chunk of text (a block of code, the contents within parentheses, array brackets, within quotes, etc) as a unit and the act on it (select it in visual mode, copy it, cut it, change it, use it as input to a search command, etc.) is a huge benefit.</div>
<div>
<br /></div>
<div>
And just like <a href="http://kent-anderson-software.blogspot.com/2014/06/why-i-can-stop-using-vim.html">I said in that article last year</a>, returning from my excursion to Vim feels like coming home. It's like drinking water, or breathing air. What I said last summer still applies...</div>
<div>
<br /></div>
<blockquote class="tr_bq">
<span style="background-color: white; font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: 13px; line-height: 18px;">Here's where they all fell short, and what [takes] me back to Vim every time...</span><br />
<ul style="font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: 13px; line-height: 18px; margin: 0.5em 0px; padding: 0px 2.5em;">
<li style="margin: 0px 0px 0.25em; padding: 0px;">hjkl for navigation <i>(EDIT: 'hjkl' is really an identifier for the whole concept of using plain old keys to do what you need to do. No special modifier keys are necessary.)</i></li>
<li style="margin: 0px 0px 0.25em; padding: 0px;">/ for searching (really, no other editor has made it this simple)</li>
<li style="margin: 0px 0px 0.25em; padding: 0px;">m to bookmark a spot, ' to hop back to it</li>
<li style="margin: 0px 0px 0.25em; padding: 0px;">and ctags</li>
<li style="margin: 0px 0px 0.25em; padding: 0px;">q to record a macro; @ to execute it again</li>
<li style="margin: 0px 0px 0.25em; padding: 0px;">1G to go to the top of the file, G to go to the bottom <i>(EDIT: commenters helped me relearn this one: gg will do the same thing, cutting out the need to hit the Shift key).</i></li>
<li style="margin: 0px 0px 0.25em; padding: 0px;">^, $ to go to the front, or end, of a line.</li>
<li style="margin: 0px 0px 0.25em; padding: 0px;">Text Objects! Holy cow, if you're a Vim user and you're not using these, you're missing something special.</li>
<li style="margin: 0px 0px 0.25em; padding: 0px;">Platform ubiquity. (Mac, Linux, Windows - Vim works great on all the platforms I use day in and day out.)</li>
</ul>
<br style="font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: 13px; line-height: 18px;" />
<span style="background-color: white; font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: 13px; line-height: 18px;">What it boils down to, really, is that the above keys have become a part of me. They are so ingrained in my brain and reflexes, that I think of what I want done in the text and my hands just naturally do it for me. It's like drinking water. I don't have to think about how to go about bringing the cup to my mouth and tipping it just right. I just do it, and keep going on about my business.</span></blockquote>
<div>
Please don't hate me because I wander. It's just more validation that Vim can stand up to <i>any</i> competition.</div>
<div>
<br /></div>
<div>
I don't claim to have the most dazzling .vimrc. But if you're interested, you can see mine on github: <a href="https://github.com/kornerstoane/.vim">https://github.com/kornerstoane/.vim</a>. Some of it is really old. I'd love to hear about any recommendations for changes to it.</div>
Kent Andersonhttp://www.blogger.com/profile/14632601949492972207noreply@blogger.com1tag:blogger.com,1999:blog-6562767159607130103.post-59084946279385813252015-01-31T11:19:00.000-07:002015-01-31T11:19:31.117-07:00Learning From Mistakes <i>Here's a good tagline for this post: Mistakes happen. They are not the end of the world. They can be fixed or compensated for. What matters most is learning from them. And, yeah, in software, proper testing is important!</i><br />
<br />
A quote <a href="http://www.wisdomword.info/thomas-john-watson-sr-8/">attributed to Thomas John Watson, Sr.</a> reads, "Recently, I was asked if I was going to fire an employee who made a mistake that cost the company 600,000. No, I replied, I just spent 600,000 training him. Why would I want somebody to hire his experience?"<br />
<br />
I appreciate that philosophy. Mistakes, when they truly are mistakes and are not the result of ill intent or a demonstrated inability or refusal to learn, should be regarded as teaching opportunities. I firmly believe that success and failure together are the best way to get a "well-rounded" education in life.<br />
<br />
This week I made a minor mistake at the day job. I renamed all the internal groups in our JIRA (issue and task management software) in preparation for synchronizing groups from our directory server. All seemed to go fine until a coworker appeared in my door and said that everyone on his team had just lost their workflow buttons. They had been there a few hours earlier. Hmmm... Turns out, JIRA refers to groups by name in lots of places, and due to the ultra-generic (flexible?) way big Java applications all seem to be designed these days, it was impossible for me to have written a script to find all these corner cases. Now that I've figured out where they were, I have been able to deal with the two or three other cases during the week where someone said they couldn't see something they were supposed to be able to see.<br />
<br />
<i>Lessons learned? First, seriously, why was I doing this in production first? Second, I learned something about the tool I would never have learned otherwise. I am better for it, and therefore my company can be better for it as well. Third, most mistakes are are not unrecoverable. And fourth, even if a mistake is unrecoverable, there is always a way forward from it, and things can get back on track.</i><br />
<br />
Ok, I could end this post right here, and you might judge it to be a nice little piece of possible wisdom that most people already share. But, like I said, this was minor mistake. I've been the cause of some major headaches, mostly very early in my career. Read on for some entertainment...<br />
<br />
<span style="font-size: large;">What happens when I delete the C:\DOS directory?</span><br />
<br />
I used a student loan and grant money to buy my first computer in college (a 386SX with 2MB RAM and an 80MB hard drive, I think this was 1992). This way I wouldn't have to spend hours upon hours in the computer labs to work on assignments, and could be home with my beautiful wife. Having a computer at home gave me plenty of opportunities to tinker and try things. Curious about what would happen, I one day decided to delete the C:\DOS directory. Imagine my surprise when the computer wouldn't boot after that. I learned a little more about what an operating system actually does (I was in my first year of computer science at this point). After a little time panicking, seeing my wife's realization that I might have just thrown away $2000 only added to the horrible feeling in the pit of my stomach. In desperation, I put in floppy disk #1 and tried rebooting. Relief! It booted and offered to install DOS again. More learning.<br />
<br />
<span style="font-size: large;">Too much whitespace in the code in a 24-line 3270 terminal is a problem</span><br />
<br />
Fast forward a couple of years. I am now working as a student programmer in the university's financial department, assigned to work on the payroll system, among other things. One day, upon arriving at work, the payroll team lead asked me if I had tested my recent changes thoroughly enough. Of course, I thought I had, but... Accounting had called. Somehow, this payroll was double what it should have been.<br />
<br />
Here's what the problem was. We accessed the development environment on the mainframe via 3270 terminal emulators from our PCs. When paging up and down, the editor leaves one line from the previous page visible to help provide some continuity between the pages. I knew at one point in the logic I was updating I would need to make the call to the ApplyPayment subroutine (or whatever it was called). I found the place in the code where I thought it should be called and added the appropriate line. I was right about it being the proper place for the call, because the call was already in that place, but it had scrolled off the screen. The line that was carried over from the previous page was a blank line, and I was only barely scanning the code as it was scrolling by. There were two or three extra blank lines after the call, which increased the chances that the line I needed to see would not be the line that carried over from the previous screen.<br />
<br />
Luckily the accountants caught it before any real damage was done. Every payroll run gets audited (thankfully) before releasing it to the printers and the direct deposit systems. A quick fix, a re-run of the payroll, and a few hours later, the checks were in the mail.<br />
<br />
<i>More learning. Testing is important. Good test data is important. But here's an observation. It's difficult to teach college students how to write good tests when they don't have enough experience to understand all the things that usually go wrong in software. Experience is so important to becoming competent in whatever you're doing in life.</i><br />
<br />
<span style="font-size: large;">Where did all the buildings go?</span><br />
<br />
Fast forward another couple of years. The payroll mistake didn't cause me to be tossed from the career path, doomed to a life of ___(insert_your_least_favorite_menial_job_here)___. Just before graduating, they offered me full-time employment. <i>(I should add that although I'm only highlighting mistakes in this post, I worked my tail off and tried to learn all I could. That overcomes a multitude of sins.)</i><br />
<br />
I was now in charge of the capital equipment inventory system, which is a fancy accounting term that means watching the value of buildings and desks and things go down, usually for tax reasons. I don't even remember what change I was making to the system that day. I coded it up and tested it in the development database, and upon seeing that it did what I expected it to do, promoted it to production.<br />
<br />
The next morning, another more experienced developer, the one whose role I had taken over on this part of the system, asked me whether I had tested the changes I had made. I said, Yes. To which he responded, "Oh, maaaan. Something went really wrong last night." More than half of all the capital equipment (the buildings and everything bolted to the floors in them) was missing from the database. It turns out there was a condition in the data I hadn't accounted for, and that wasn't represented in the dev data we had to test with. Crud. I spent 36 straight hours at the office trying to reconstruct the data, but there wasn't enough there to reliably recreate everything. Month-end was coming, and pressure was high.<br />
<br />
What's that? Just restore from the backup? Yeah. After a day and half of trying to avoid doing that, we finally turned to the operations department to request the restored data. After a few hours of closed door conversations, they came sheepishly back and said that their backup job had been failing every night for the last six months. The operators had been ignoring the messages on the console. After so long, they just started telling each other that message always happens so it doesn't mean anything. College students. You could sort of give them a bit of a pass, since most of them didn't really know what those messages meant anyway. But the system admins? Never, in six months, did they bother to review the logs?<br />
<br />
At this point, yelling and screaming accomplishes nothing, and the business folks over in the administration building were pretty calm people. The accounting director decided to close the month early, using data manually entered from the last report, two weeks old. I fixed my bug and everything proceeded forward from there. Harm was done. Time was lost. Multiple people contributed to this disaster.<br />
<br />
<i>What did I learn? Again, testing is important. And having good test data is critical. And if you have any sysadmin responsibilities, it's a good idea to develop a habit of surveying the logs every day as a sanity check. And, if you're going to work in a job, be proactive and learn all you can about what you're working with.</i><br />
<br />
These whoppers all happened 20+ years ago. To be sure, I have continued to learn a few things by not doing them right the first time. But nothing has really come close to the levels of disaster that I have caused, or potentially caused, in those early years. I suppose this is what we call EXPERIENCE. Life would have been better in those occasions had I not made the mistakes I did. But knowing what can go wrong, really knowing it, makes me more capable now. The only real mistake in life is not learning from the experience when it doesn't go right.<br />
<br />
<br />
<br />
<br />
<br />Kent Andersonhttp://www.blogger.com/profile/14632601949492972207noreply@blogger.com0tag:blogger.com,1999:blog-6562767159607130103.post-63660634256380105732015-01-17T12:49:00.000-07:002015-01-17T13:39:47.499-07:00Monitoring Web Service Performance with Elasticsearch, Logstash and Kibana<div>
Elasticsearch, Logstash and Kibana (<a href="http://www.elasticsearch.org/">www.elasticsearch.org</a>) are fantastic open source products for collecting, storing, monitoring and analyzing events.</div>
<div>
<br /></div>
<div>
Here is one way you can configure Logstash and Kibana to monitor system loads and web application response times. (Elasticsearch, while powerful, does its basic job so well that I did not need to configure anything for it in this example, other than to set it up so Kibana could connect to it, which Kibana told me how to do the first time I tried.)</div>
<div>
<br />
<b><span style="font-size: large;">Configuring Logstash</span></b><br />
<br /></div>
<div>
<i><b>Note:</b> This configuration works on Ubuntu servers. Your environment may need slight adjustments.</i><br />
<br /></div>
<div>
<pre class="brush:ruby">input {
# System Load
exec {
# We are generating our own events here, so we
# get to assign a type of "system-loadavg" so
# Logstash can apply a specific filter to them.
type => "system-loadavg"
# Produce the system loads on a single line.
# Example:
# 0.23 0.18 1.03
command => "cat /proc/loadavg | awk '{print $1,$2,$3}'"
# Do this every 60s while Logstash is running.
interval => 60
}
# Web Application Response Time
exec {
# Again, generating our own events, with type
# "ws-ping"
type => "ws-ping"
# (Optional) Add a field to our custom event. If
# you monitor more than one service this way, you
# will be able to distinguish them using this field.
add_field => { "service" => "jira" }
# Execute a couple of commands to get a web
# page from JIRA
# /usr/bin/time Uses the Linux time command,
# not the Bash built-in function
# -f '%e' Specify the output should just
# be the real time, in seconds.
# 2>&1 Redirect time's output from
# stderr to stdout for Logstash
#
# curl Just google this for an idea
# of its sheer awesomeness.
# -s Silent mode, don't want the
# progress bar in the output.
# -k Don't check SSL certificate
# (this is acceptable for our
# self-signed internal cert)
# -o /dev/null Send the output to /dev/null.
# We don't care what the page
# contains, just how long it
# takes.
# -u someuser:somepass
# We do not want to measure the
# static login page, so we'll
# need curl to authenticate with
# JIRA so it can get to a dashboard.
# https://our.jira.host/jira/
# The page to load.
#
command => "/usr/bin/time -f '%e' curl -sk -o /dev/null -u someuser:somepass https://our.jira.host/jira/ 2>&1"
# Do this every 60s while Logstash is running
interval => 60
}
}
filter {
# Transform the system-loadavg event by adding names
# to the numbers
if [type] == "system-loadavg" {
grok {
# Parse/match the event using the pattern
# of three numbers separated by spaces. Map
# the numbers to float numbers in Elasticsearch
# (otherwise, we won't be able to plot them.)
match => { "message" => "%{NUMBER:load_avg_1m:float} %{NUMBER:load_avg_5m:float} %{NUMBER:load_avg_15m:float}" }
# Only store matched values that have names
# provided for them (this is the default)
named_captures_only => true
}
}
# Transform the ws-ping event by adding a name to
# the number created by the input event.
if [type] == "ws-ping" {
grok {
match => { "message" => "%{NUMBER:responsetime:float}" }
}
}
}
output {
# Send all events to Elasticsearch. Be sure to configure
# the correct host.
elasticsearch { host => "your.elasticsearch.host" }
}
</pre>
<div>
<br />
Logstash will now generate performance monitor events every minute and send them to Elasticsearch.<br />
<br /></div>
<div>
<br /></div>
<div>
<b><span style="font-size: large;">Visualizing in Kibana</span></b><br />
<br />
Here's how to configure your Kibana dashboard to watch and visualize these events.</div>
<div>
<br /></div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-0TDUFK0ZD9k/VLqqyyZdIYI/AAAAAAAAAHU/cnxRb_vvF2U/s1600/ELK-performance-load.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-0TDUFK0ZD9k/VLqqyyZdIYI/AAAAAAAAAHU/cnxRb_vvF2U/s1600/ELK-performance-load.png" height="105" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-k0BijeK3Uyw/VLqqy4pPlMI/AAAAAAAAAHQ/1aB90AFMrys/s1600/ELK-performance-responsetime.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-k0BijeK3Uyw/VLqqy4pPlMI/AAAAAAAAAHQ/1aB90AFMrys/s1600/ELK-performance-responsetime.png" height="114" width="200" /></a></div>
<br />
<span id="goog_544687489"></span>
Add the following queries to your dashboard, as two separate queries. You can optionally set the color and alias for the queries so they look better on the screen. This is done by clicking the color dot in the query field in Kibana.<br />
<br />
<pre> host:our.jira.host AND type:system-loadavg</pre>
<pre></pre>
<pre> type:ws-ping AND service:jira
</pre>
<br />
Notice that Logstash automatically adds a "host" field to the event. You can see that our custom event types and fields have made it, too.<br />
<br />
To graph the system load average, add a new panel to your dashboard with the following parameters.<br />
<br />
<b>Panel type:</b> Histogram<br />
<b>Title:</b> 5 Minute Load Average<br />
<b>Chart Value:</b> mean<br />
<b>Value field:</b> load_avg_5m (We named this in the Logstash configuration)<br />
<b>Chart option:</b> Line (not bars), not stacked<br />
<b>Queries:</b> selected, then select the query that corresponds to the system-loadavg search you defined above. It will be in the list Kibana provides for you to select.<br />
<br />
To graph the response time, do the same thing as above, but select the responsetime field for the Value field, and the query that corresponds to the ws-ping event type as defined above.<br />
<br />
That's it!<br />
<br />
Here are a couple of things I learned along the way.<br />
<br />
<ul>
<li>Kibana didn't update my charts at first. The updating icon on each panel just sat there, spinning. The problem was that I hadn't told Logstash to send the numbers into Elasticsearch as numbers, so it sent them as strings. Kibana was getting errors back from Elasticsearch, but wasn't telling us anything about it. Using the Inspect tool (the "i" icon on the panel) gives you a curl command to see the result of the query for yourself.</li>
<li>Kibana didn't give us the option of plotting the actual value, so we use the mean to provide the closest representation of the value over the time between plot points. It's a good compromise since it smoothes the plot somewhat.</li>
<li>We collect more events from our application logs and Linux system logs. Applying a grok pattern to those was more difficult, and the docs could be better. But there is a <a href="http://grokdebug.herokuapp.com/">Grok Debugger</a> that allows you to paste in a sample of your event data and play with the grok patterns to see what it is able to match. Once I found this, my work for the week was done in no time at all.</li>
</ul>
<div>
This is a short intro to a very powerful event management platform. I hope it helps you get up and running a little faster.</div>
</div>
</div>
<script type="text/javascript">
SyntaxHighlighter.all()
</script>Kent Andersonhttp://www.blogger.com/profile/14632601949492972207noreply@blogger.com5tag:blogger.com,1999:blog-6562767159607130103.post-47045231634425872932015-01-06T16:49:00.002-07:002015-01-06T16:49:36.230-07:00Paying the Indie Dues and Simplifying My LifeLet's be honest. It's lucky for me you happened by my little blog. I'm nobody of any sort of fame. I wouldn't say that I'm one of the world's, or the industry's, or even my circle of friends', best thinkers. I'm just a regular person. A software developer. I have a day job that more than pays the bills. I'm really happy there.<br />
<br />
Why, then, would I dream of becoming an independent software developer? It must be because the life of an indie is so glamorous. I mean, who wouldn't love working from anywhere you want, on whatever you want. And who wouldn't want hundreds of thousands of dollars thrown at you each year as the general population of the world falls all over themselves to pay you a couple of bucks to have the pleasure of using your beloved software creation?<br />
<br />
Oh yeah. Apparently, it's not like that.<br />
<br />
I am thoroughly impressed by indie developers that can make a living at what they're doing. They work hard and most of them enjoy none of the benefits mentioned a couple of paragraphs ago. But they're making a living. I listen to many of them on podcasts, and one thing I've come to understand (we all <i>know</i> it, but we don't all <i>understand</i> it) is that their success is the result of years of working at it.<br />
<br />
If you look at <a href="http://kent-anderson-software.blogspot.com/p/my-apps.html">My Apps</a>, you'll see that I've developed a couple of games for iPhone and iPad. They're fun, or so I'm told by all my family and friends who have played them (not all my family and friends, mind you, just the ones who have played my games). I admit I had dreams of going viral and retiring on it, or at least paying off my mortgage and sending my kids to college on a TreeDudes scholarship. The reality is, they're only my second and third attempts at publishing indie software. My first attempt was about 12 years ago, when I wrote a hex file viewer for Palm OS and sold two copies of it via Handango. So, between that app, and my two recent iOS games, plus the ad revenue from the games, I have earned so far a grand total of about $40. I suppose the dues to be paid for success are somewhat higher than where I am currently at. It's all good. I'm not angry or disillusioned. Of course I'm disappointed, but it's the same kind of disappointment you experience when you buy a raffle ticket and you don't win. What else would I expect?<br />
<br />
<a href="http://articles.economictimes.indiatimes.com/2011-07-20/news/29794883_1_niklas-hed-angry-bird-mighty-eagle">Rovio produced 51 titles</a> before Angry Birds made them instantly famous. <a href="http://david-smith.org/apps/">David Smith</a> (@_DavidSmith) has a whole portfolio of apps he maintains. <a href="http://www.red-sweater.com/">Daniel Jalkut</a> of Red Sweater does, too. His podcasting partner, <a href="http://www.riverfold.com/">Manton Reece</a>, is the same. <i>(Can you tell which podcasts I listen to the most?)</i> Interestingly, Manton's app business is his side job. He has mentioned often in his <a href="http://www.coreint.org/">Core Intuition</a> podcast that he has a day job, too. <br />
<br />
Still being honest... I did more than just dream of wild success. I prepared for it. I registered my own domain, formed an LLC, got an employer ID number (EIN) from the IRS, opened a business account at the bank, and converted my personal Apple Developer account to a business account. After all, I had to look legitimate so nobody would realize I was just a small potato.<br />
<br />
Looking back, I can now understand why my wife (she is so patient and understanding) always looked at me and said, "Well, I don't think all that is necessary, but you're the one who knows about these things, so I trust ya." $40 in revenue doesn't even come close to covering the hosting fees, domain registration fees, Apple Developer fees, and small account fees at the bank. And, oh yeah, I had to pay extra for Turbo Tax to help me do my small business taxes, since I had a small (very small) business. But, after operating on a loss, at least my tax bill was in my favor!<br />
<br />
I didn't need any of that stuff to publish my software. I've learned a valuable lesson. I'm letting my domain expire. It was always a bit of a pain having to explain that it was "cornerstone" but spelled "kor-ner-sto-ane." We did that so the domain name would be unique. Yep. The only thing I hosted on that site was this blog, which was easily moved over to Blogger.com and I actually like the platform. It's nice not having to worry about Wordpress updates anymore. My LLC has been terminated. It's not like it was really going to protect me from a lawsuit, anyway. The bank account is closed, eroded entirely away by low-balance fees. Oddly enough, the EIN gets to stay around forever. I'm sure I'll forget about it someday. And, yes, I've opened a new Apple Developer account in my own name again since Apple doesn't convert business accounts back to personal accounts, darn it.<br />
<br />
I am not giving up on developing my own little portfolio of apps, though. I've got a few ideas that I'm thinking about. One of them will soon be moved from the back burner to the front, and I'll be off developing something new again. But before I start that, there's an awful lot of home repair and yard work that needs to happen! <br />
<br />
I am not foolish enough (anymore) to hope for overnight success, but I am still counting on life providing for me what it does for others who pay the price required for success. Success takes steady, consistent work. As I consider it now, maybe the reason I have a good day job is because I've worked at it nearly every day for two decades. Gosh, imagine that!<br />
<br />
I really do feel fortunate that you've read my blog post all the way to the bottom. Thanks for that. I'd love to hear from you in the comments below.Kent Andersonhttp://www.blogger.com/profile/14632601949492972207noreply@blogger.com2tag:blogger.com,1999:blog-6562767159607130103.post-51093751186134614442014-08-13T13:34:00.000-07:002015-01-04T10:01:58.053-07:00Playing With Swift: ExtensionsI am finally getting around to playing with Swift, <a href="http://developer.apple.com/swift/">Apple's new language</a> for Mac and iOS development. I have to say, I am excited to have a new language to learn. Swift looks very interesting as it pulls in many great features of other modern languages.<br />
<br />
Like <a href="https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html">Objective-C</a>, and <a href="http://ruby-doc.com/docs/ProgrammingRuby/html/classes.html#UD">Ruby</a>, and probably others that I don't know about, Swift allows us to add functionality to any class, including the built-in classes like String and Array.<br />
<br />
Here are a couple of examples of extending the Array and the String built-in classes to add a join() method.<br />
<br />
First, let's extend the Array class to add a join(sep:String) method that returns a string containing all of the elements of the Array, delimited by the separate value passed in.<br />
<br />
<pre><code class="swift">extension Array {
func join(sep:String) -> String {
var s = ""
if let firstItem = self.first {
s = "\(firstItem)"
for i in 1..<self.count {
s = s + sep + "\(self[i])"
}
}
return s
}
}</code></pre>
<br />
Pretty cool, right? But what if our religion thinks it's better to extend String and pass in the list to be joined (Python style)?<br />
<br />
<pre><code class="swift">extension String {
func join<T>(list:Array<T>) -> String {
var s = ""
if let firstItem = list.first {
s = "\(firstItem)"
for str in list[1..<list.count] {
s = s + self + "\(str)"
}
}
return s
}
}</code></pre>
<br />
Just as easy. Did you notice the slight variation to use a slice of the array, rather than a for-loop?<br />
<br />
While I love a good pedantic discussion about which class is more "proper" to extend, you probably don't have time for such things, so let's just extend them both. And to keep it <a href="http://wikipedia.org/wiki/Don't_repeat_yourself">DRY</a>, let's alter our Array extension to call the String extension via the separator argument. (Yes, I've been influenced by Python.)<br />
<br />
<pre><code class="swift">extension Array {
func join(sep:String) -> String {
return sep.join(self)
}
}</code></pre>
<br />
One enhancement I would like to make to these methods is to use a default parameter value for the separator. Unfortunately, as of this writing, defining a default parameter causes a segfault in Xcode. It <strong>is</strong> beta software, after all!<br />
<br />Kent Andersonhttp://www.blogger.com/profile/14632601949492972207noreply@blogger.com0tag:blogger.com,1999:blog-6562767159607130103.post-38679457272860105512014-08-09T07:12:00.000-07:002015-01-04T10:00:38.286-07:00The Real Value of Writing Automated TestsThe product manager asks in the standup, <em>"Why did this start to fail? Don't we have tests that cover this?"</em><br />
<br />
<em>"Yes, we have tests. They all passed,"</em> we respond.<br />
<br />
<em>"Then your tests aren't very good."</em><br />
<br />
Well, maybe. It's true, we could write a lot more tests. In general, if your software team is committed to automated testing, you'll write at least as much test code as application code. Project estimators should plan on writing two applications - the one with all the features, and a second one to test the first one.<br />
<br />
In our case, the unit tests were all passing, but some of the integration tests started failing. These are the tests that verify the interactions among the various parts of the application. Of course, each time we find a new situation where a test fails, we try to figure out why, and cover that scenario with yet another test. In this way, what evolves is a system that informs us when our software parts interact in ways we didn't expect.<br />
<br />
Or maybe it devolves? It takes <strong>a lot</strong> of discipline in a team (even a team of one) to keep your testing system clean and orderly. Often it becomes a hodgepodge of bolted on test cases that can start disagreeing even with themselves! We've all been victims of someone else's failure to tearDown() completely.<br />
<br />
Even in mature projects, the tests don't seem to catch everything, no matter how thorough we think we've been.<br />
<br />
<strong>So why go to all the extra effort? If the tests require as much maintenance as the application itself, what value do they really bring to the project?</strong><br />
<br />
First, tests make sure existing interfaces between parts of the application remain consistent.<br />
<br />
Second, tests make you as a developer consider the more subtle interfaces than you might not have been aware of. If you consider that your module's interface might be more than just the method signature and return value, you begin to understand some of these more subtle interfaces <em>"Whoa, why did that fail? I didn't even touch that?!"</em> It could be because that code you just refactored had a side effect that other code has come to depend upon. Oops. Good thing the test caught that. Of course, sometimes the tests don't catch every subtle interface impact. But they work often enough that their value to the project cannot be disclaimed.<br />
<br />
Finally, and in my opinion, most importantly, when tests are automated (and hopefully executed by a continuous integration system), they keep you from cutting corners when it becomes crunch time. I've developed software under various methodologies. Regardless of the individual tenets of each methodology, they always seem to slip into a "just get it done" mode as milestones approach. Often, when a trouble ticket arrives, the developer jumps right in, makes a quick fix, commits the code, gets a buddy to review it, merges it into the head (tip, trunk, whatever), then receives the email from the CI server that they broke the build. The automated tests catch the fact that the developer didn't think about everything they should have thought about when they made their change.<br />
<br />
I once worked in a team that rewarded the build-breaker with a rubber chicken that had to hang over their cubicle until the build turned green again. Yes, I flew the rubber chicken a lot. My role in that team was mostly in the infrastructure of the application, so when I goofed up, it broke a lot of things. Fortunately, we had a decent test system in place, and the CI server let me know about my shortcomings very quickly. So in crunch time, when everyone's focus is naturally narrowed to a dangerous tunnel vision, our automated testing system forced us to widen our vision and take a breath, and think more broadly about everything our simple little change might affect.<br />
<br />
<h2>
Summary</h2>
<ul>
<li>Testing is hard work. Plan on writing two applications.</li>
<li>Testing helps maintain a consistent interaction between software modules.</li>
<li>Automated testing helps you consider more of the overall effect of your change when under pressure.</li>
</ul>
And finally,<br />
<ul>
<li>Fly a rubber chicken in the team. It's a lot of fun. The look on other teams' managers' faces when they walk down the hall is priceless.</li>
</ul>
Kent Andersonhttp://www.blogger.com/profile/14632601949492972207noreply@blogger.com0tag:blogger.com,1999:blog-6562767159607130103.post-8447545509674058532014-07-12T06:28:00.000-07:002015-01-04T09:59:34.669-07:00Thoughts on Software Development Job Interviews<b>TL;DR</b> <i>I interview people for programming jobs. I'm turned off by a lack of confidence, by an inability to talk tech with me, by profanity, and by an unwillingness to try when facing something new. I'm impressed by energy, enthusiasm, and competence.</i><br />
<br />
My dream career is one in which I work for myself, creating software that people enjoy and find useful, and earning enough to provide a comfortable lifestyle for my family. For me, this will be a work in progress as I've already tried the "cut all ties on Friday and start a new career on Monday" thing. It didn't work for me, largely because I jumped into something I was unprepared to do, and lacked any real passion for. (For the curious, I lived in poverty for five years trying to start a financial advising practice working for Edward Jones.) When that little side trip ended, I ran back, wholeheartedly to the software development world, and yes, the passion had returned. My next shot at setting my own priorities and timelines will be aligned with what I have energy to do every day. I like creating things and I like writing (although I hate writer's block). But I digress...<br />
<br />
In my current day job I work as a senior software engineer at a small company to which larger companies outsource development jobs. There are a lot of benefits to working at this company. Foremost, I don't have to switch jobs in order to do something different. As a contract company, we get lots of new opportunities every year, most of them are interesting, and a lot of them are fun. We've got a great culture, and lots of people want to come work for us. As a team leader, I have the privilege of interviewing candidates on a regular basis. I've interviewed new grads and people with much more experience than I have, some arrogant jerks and some humble giants. I am actually easy to impress -- show me you're a nice person, you're willing to work, and that you're competent in the types of work we will be expecting you to do.<br />
<br />
Here is a short list of things that can make or break you in a job interview.<br />
<br />
<h2>
Please don't apologize for taking our time</h2>
Everyone feels a little inadequate sitting on that side of the table. One candidate prefaced (and closed) the interview with a polite...<br />
<blockquote>
I know I'm not the most qualified candidate, but thank you for taking time to interview me anyway.</blockquote>
If you didn't think you had a shot, why did you apply? Without revealing too much about this case in particular, it was a person who had been out of the business for a while (for a good reason), and was trying to get back into it. They had skills and knowledge that were still relevant, but their complete lack of confidence was a deflater. Now, several months later, that's pretty much all I remember from the interview.<br />
<h3>
My recommendation</h3>
When interviewing for a job for which you feel underqualifed, there are two things that would impress me.<br />
<ol>
<li>Spend some of your spare time getting up to speed on the skills you're going to need. Get a book. Play around. Make something. Anything.</li>
<li>Address your perceived lack of skill early by saying something like, "My skills are a little rusty, but I've been tinkering with things in my spare time, and I'm anxious to dive into it full time."</li>
</ol>
<h2>
Please try to give a good answer</h2>
We work in a profession that is geared toward solving problems. It should be expected that there would be a problem solving question at some point during the interview. If we do ask you to solve a problem, please try. One candidate simply said...<br />
<blockquote>
I have no idea how to do that.</blockquote>
Understandable, it may be something you've never considered before. Fair enough. We offered to let them think about it for a minute before replying. They wouldn't. They simply replied the same.<br />
<blockquote>
I wouldn't even know where to start. I'm sorry.</blockquote>
At that point, the interview essentially was over. The rest was just being polite. If I'm going to have to work with you on a daily basis, I want to know you're going to try to do your share of the work.<br />
<h3>
Recommendation</h3>
I personally don't fault someone for not knowing how to solve a problem they've never considered. The following responses impress me.<br />
<ol>
<li>Can I think about this for just a minute?</li>
<li>Are there any tools available that could accomplish a specific part of a potential solution? <em>This question shows you've started to think about a possible solution, but may not be aware of everything in your tool chest.</em></li>
<li>Can I make a few assumptions? <em>Sure. This shows you're still trying to wrap your head around the problem. It shows you're trying.</em></li>
</ol>
<h2>
Don't say you know something you do not know</h2>
Résumés are great sales pieces. Good ones give us a good sense of the work you've done. When listing skills, please be sure you have at least some level of competency with them. If you say you've been a C programmer, for example, please be prepared to answer questions about pointers, stack and heap, integer overflow, and so forth. This is basic stuff.<br />
<h3>
Recommendation</h3>
If you mention it in your résumé, please be prepared to talk about it in depth.<br />
<h2>
Show energy and passion</h2>
Some folks (myself included) get passionate about things like text editors and operating systems. That's not necessarily the kind of passion I'm talking about (although I would love to have a candidate talk about those things with me for a few minutes).<br />
<br />
I recall one interview where we asked the candidate about jobs he listed in his résumé. He spoke in great detail and with fondness about a project he had worked in over 10 years ago. It impressed me that he remembered the details and spoke of them energetically. He was proud of the work he had done. I liked that.<br />
<h2>
Avoid profanity</h2>
Disclosure: I don't like the use of profanity. To me, it seems like a lazy way to express strong feelings.<br />
<br />
There's a little dance that is done in the workplace when you meet someone new. For the first little while, everything is clean and sober. Then one of the parties will lob a soft one over to see how it's received. A "D" or an "H", maybe even an "S". If it's well received (or at least not acknowledged as inappropriate), they'll quickly move to dropping an F-bomb. To me, it just seems so unprofessional. Shockingly, this dance has even been done in job interviews. Sadly, it's often initiated by our side of the table. For that, I can't really blame someone who would naturally assume it was an invitation to relax. Just know this, it won't impress me in any way, other than to think you should probably work harder at broadening your ability to communicate effectively.<br />
<h3>
Recommendation</h3>
Keep it clean, even when "invited" to relax by the interviewer. It's likely nobody will ever remember that you didn't use foul language in a job interview. But someone will probably remember if you did.<br />
<h2>
Wrap up</h2>
This post simply sums up some of the things that I have seen strengthen or weaken a candidate during an interview. All of this is my opinion. Others will definitely have different opinions. Feel free to add them at the bottom.Kent Andersonhttp://www.blogger.com/profile/14632601949492972207noreply@blogger.com0tag:blogger.com,1999:blog-6562767159607130103.post-72704068255854039402014-06-28T11:05:00.000-07:002015-01-04T09:55:16.339-07:00Why I can't stop using VimIn 1992 or 1993, my brother-in-law hired me to help him with some maintenance on a software system he had written for a customer. It ran on SCO Xenix.<br />
<br />
"Hey, Bro, why doesn't 'EDIT' work on this computer?" I was a DOS rookie back in those early days, having just started my major in Computer Science.<br />
<br />
"Um, there's no EDIT command on Xenix. Let me introduce you to vi. You use hjkl to move around, plus a few other keystrokes. Once you get used to it, it's not that bad." I swear he had a sly grin on his face as he said that, but he was serious. Vi was what I was going to have to use. Only now, as I write this, do I recall that he was, at that time, working in Windows with a shiny graphical editor. Oh, well, he was the boss, after all.<br />
<br />
Anyway, I learned to move around pretty quickly, hitting 'i' to start typing, and 'Esc' to move around again. I wouldn't say I was a wizard, but I got the work done. We would tap away on Wednesday nights and Saturday mornings in our work sessions, while our wives played Super Mario Brothers out in the front room. Good times. I really miss it sometimes.<br />
<br />
That was the side job. The day job kept me programming on OS/2 and some Windows. The stock editor on OS/2 was a decent editor - you typed text, and it saved it just fine. I cranked out many, many lines of code using that editor.<br />
<br />
We're programmers. Editing text is the very essence of what we do. We get excited about these sorts of things.<br />
<br />
Fast forward to 1997. My brother-in-law once again helped me get my foot in the door at a small telecommunications company in Phoenix. My first workstation was a Sun SPARCstation 20, running Solaris. Cool, I thought, I can use vi to get going. But by now, everyone was using UltraEdit on their Windows machines. I didn't have one of those, but I really needed the productivity boost of a better editor. A little Yahoo searching turned up some vi clone called <a href="http://www.vim.org/" title="Link to Vim">Vim</a>. There was a new release of it, version 5, and it came with syntax highlighting! Take that, UltraEdit!<br />
<br />
This is getting long... Let's just say, I used Vim for all my editing on that SPARC station. And when I finally got a Windows PC, I was elated to find that there was a Windows version of Vim, with a GUI and everything!<br />
<br />
Vim and I have had a bit of a fickle relationship over the years. Although it does an amazing job helping you type text, it's not flashy, and it's not perfect, and because of those imperfections, I strayed from time to time, looking for something to fix those imperfections, and still give me the second-nature editing reflexes I had come to enjoy from Vim.<br />
<br />
Here's a list of the things that have caused me to look beyond Vim.<br />
<ul>
<li>viml - Vim's built in scripting language is tedious to work in. Some folks have done some amazing things with it, but, try as I have, I can't get myself to enjoy it.</li>
<li>Glossy finish - I admit, I'm guilty of a little editor envy when I see some of the prettier interfaces out there, especially these days.</li>
<li>Emacs's Org Mode - this is such a killer emacs app. Others have tried to replicate it for Vim. Someday.</li>
<li>Context-aware code completion - OmniComplete provides a decent framework, but nobody has really matched the power of Visual Studio, Eclipse, or Xcode.</li>
</ul>
<br />
Yes, I have strayed, and flirted with many other editors...<br />
<ul><br />
<li><a href="http://www.jedit.org/">JEdit</a> (Ugh, looks like a Java Swing App, and too much clutter everywhere)</li>
<li><a href="http://www.gnu.org/software/emacs/">Emacs</a> (Love the features and the eLISP, but can't tolerate the myriad key chords needed to navigate around my files.)</li>
<li>Emacs + <a href="https://gitorious.org/evil/pages/Home">Evil Mode</a> (Evil mode is REALLY good. The only thing that made me frown was poor code navigation via ETAGS.)</li>
<li><a href="http://armedbear-j.sourceforge.net/">J</a> (A tiny little Java based editor, built around ABCLisp from Armed Bear. I really loved this editor, but it fell behind in features, and I needed more power.) EDIT: This editor is still alive (somewhat) on github: <a href="https://github.com/kevinkrouse/j/tree/master/j">https://github.com/kevinkrouse/j/tree/master/j</a>. Thanks to bokchoi on ihackernews.com for the link.</li>
<li><a href="http://www.pnotepad.org/">Programmers Notepad</a> (Decent editor, but not enough features).</li>
<li><a href="http://editra.org/">Editra</a> (a Python/WxWindows based editor, with Vi emulation. But it also lacked things like smart indent and other features I have come to take for granted.)</li>
<li><a href="http://notepad-plus-plus.org/">Notepad++</a> (A very respectable editor. Tons of features. But for some reason, I just don't enjoy using it. Maybe it's all the visual clutter around the borders of the screen?)</li>
<li><a href="http://www.slickedit.com/">Visual SlickEdit</a> (I actually bought a license for this once, about a decade ago. I liked using it, but I could only afford one platform, and there are lots of free editors that do most of the same things. With Vim in my toolbox, I'll never pay $300 to edit text.)</li>
<li><a href="http://macromates.com/">TextMate</a> (When on a Mac, it had to be tried. The free TextMate 2 is a great editor, and I flirted with this one the longest. Were it not for the things I'm about to mention below, I'd be using this editor as much as possible.)</li>
<li>A whole slew of respected MacOS based editors: Kod, Smultron, SublimeText, TextWrangler, etc.</li>
</ul>
<br />
Here's where they all fell short, and what took me back to Vim every time...<br />
<ul>
<li>hjkl for navigation</li>
<li>/ for searching (really, no other editor has made it this simple)</li>
<li>m to bookmark a spot, ' to hop back to it</li>
<li>and ctags</li>
<li>q to record a macro; @ to execute it again</li>
<li>1G to go to the top of the file, G to go to the bottom</li>
<li>^, $ to go to the front, or end, of a line.</li>
<li>Text Objects! Holy cow, if you're a Vim user and you're not using these, you're missing something special.</li>
<li>Platform ubiquity. (Mac, Linux, Windows - Vim works great on all the platforms I use day in and day out.)</li>
</ul>
<br />
What it boils down to, really, is that the above keys have become a part of me. They are so ingrained in my brain and reflexes, that I think of what I want done in the text and my hands just naturally do it for me. It's like drinking water. I don't have to think about how to go about bringing the cup to my mouth and tipping it just right. I just do it, and keep going on about my business.<br />
<br />
THAT's what keeps me coming back to vim.<br />
<br />
Yes, it still has its imperfections. I end up using IDE's for a large part of the work I do, and they provide a lot of what I wish vim would do better. But there are some plugins that bring the above features into those environments...<br />
<br />
For Eclipse, I use the <a href="http://vrapper.sourceforge.net/" title="Link to Vrapper">Vrapper</a> plugin. Works great!<br />
<br />
For Xcode, I have used <a href="http://github.com/JugglerShu/XVim" title="Link to Xvim">XVim</a>. It's not perfect, and crashes Xcode in a couple of situations. But it's getting better, and is actively developed.<br />
<br />
As a special bonus for having read my lengthy article all the way to the bottom. Here's a chunk of my .vimrc that allows me to have nested "project" settings in my directory tree...<br />
<pre><code>" Search for any .vimsettings files in the path to the file.
" Source them if you find them.
function! ApplyLocalSettings(dirname)
" Don't try to walk a remote directory tree -- takes too long, too many
" what if's
let l:netrwProtocol = strpart(a:dirname, 0, stridx(a:dirname, "://"))
if l:netrwProtocol != ""
return
endif
" Convert windows paths to unix style (they still work)
let l:curDir = substitute(a:dirname, "", "/", "g")
let l:parentDir = strpart(l:curDir, 0, strridx(l:curDir, "/"))
if isdirectory(l:parentDir)
call ApplyLocalSettings(l:parentDir)
endif
" Now walk back up the path and source .vimsettings as you find them. This
" way child directories can 'inherit' from their parents
let l:settingsFile = a:dirname . "/.vimsettings"
if filereadable(l:settingsFile)
exec ":source " . l:settingsFile
endif
endfunction
autocmd! BufEnter * call ApplyLocalSettings(expand("<afile>:p:h"))</code></pre>
<br />
I posted it to Reddit a couple of years ago, and the feedback was overall positive. There were a few good ideas posted in the comments, so <a href="http://www.reddit.com/r/vim/comments/12tpf9/per_project_vim_settings/">here's the link</a>.<br />
<br />
Thanks for reading. I'd love to hear your feedback below.<br />
<br />
EDIT: spelling corrections; added links to other editorsKent Andersonhttp://www.blogger.com/profile/14632601949492972207noreply@blogger.com19tag:blogger.com,1999:blog-6562767159607130103.post-57346124196794173702014-05-01T00:42:00.000-07:002015-01-04T09:52:22.201-07:00A Simple State Pattern Applied to Game DesignWhen it comes writing software, less is better, generally speaking. The fewer lines of code you have, the lower the chance of doing something wrong. For me, this is most obvious when it comes to the number of if-statements in my code.<br />
<br />
Here's an idea for reducing the number of if-statements used to transition between game states. It's not a new idea. In fact, it's an old design pattern from the <a href="http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612">GoF Design Patterns book</a>.<br />
<br />
<a href="https://itunes.apple.com/us/app/toggalight/id869834776?ls=1&mt=8">Toggalight</a>, my second game, is small in scope, but has the usual need of presenting different UI elements, and responding to user interaction differently depending on whether the game is running, paused, or over.<br />
<br />
When I started on this project, I decided I wasn't going to stick myself into an if-statement quagmire again. So I broke out the trusty <a href="http://en.wikipedia.org/wiki/State_pattern">State pattern</a> and went to work.<br />
<br />
<h3>
Overview</h3>
The game has four buttons that cause state changes: Start, Pause, Resume, Start Over. The GameScene class implements a library of functionality that is available to both the view controller and the various state classes.<br />
<br />
<em>Note:</em> I'm using a python-like pseudo-code, and will be abstracting most of the details so we can focus on the state structure and transitions. The game is actually written for iOS in Objective-C, using SpriteKit.<br />
<br />
<h3>
BaseGameScene</h3>
This is the base class for all game states, and defines the interface expected by the GameScene.<br />
<br />
<pre><code>abstract class SceneState
property gameScene
// state management - empty base class methods that don't do anything
method enterState()
method exitState()
// Events - empty base class methods that don't do anything
method onFrameUpdate()
method onLightTouched()
method onStartButton()
method onPauseButton()
method onResumeButton()
method onStartOverButton()</code></pre>
<br />
<br />
<h3>
RunningState</h3>
I'll spare you the details of all the states, but here's the gist of what's in the most complex state, the one that's active while the game is being played.<br />
<br />
<pre><code>class RunningState : SceneState
method enterState()
// set up UI for state
gameScene.pauseButton.show()
gameScene.startButton.hide()
gameScene.startOverButton.hide()
gameScene.resumeButton.hide()
gameScene.helpButton.hide()
gameScene.calibrateButton.hide()
gameScene.highScoreLabel.hide()
// Physics are different between running and game over states
gameScene.setGameRunningPhysics()
// If this is a new game (not a resume), layout a new field
// and reset the clock. Yes, I know it's a nasty if statement.
// I'll probably refactor this in the next release.
if (gameScene.shouldReset)
gameScene.generateNewField()
// Unpause the scene
gameScene.frozen = false
method exitState()
// Do nothing. The base class defines an empty method for this
method onFrameUpdate()
// Respond to device motion (accelerometer and gyroscope)
motion = os.getCurrentMotion()
gameScene.physics.gravity = Vector(motion.roll, motion.pitch)
// Check time remaining
// Yup, more refactoring needed here, too. onClockExpired would be
// a good event to handle at the state level.
if gameScene.clock.isExpired()
gameScene.transitionToState(gameScene.gameOverState)
method onLightTouched(light)
// Toggle it off/on
light.on = not light.on
gameScene.updateScore()
// If all the lights are turned on, layout a new field
// This if-statement seems appropriate, actually.
if light.container.allOn()
gameScene.generateNewField()
method onPauseButton()
gameScene.transitionToState(gameScene.pausedState)</code></pre>
<br />
<br />
You get the idea. The other states follow the same pattern, doing what they're supposed to do when the events are triggered.<br />
<h3>
GameScene</h3>
Here are the relevant parts of the GameScene class.<br />
<br />
<pre><code>class GameScene
property preStartState
property runningState
property pausedState
property gameOverState
property currentState
method init()
// Lots of code to set up the scene.
preStartState.gameState = self
runningState.gameState = self
pausedState.gameState = self
gameOverState.gameState = self
transitionToState(preStartState)
method transitionToState(newState)
// Objective-C lets me get away with not checking for
// null first. Lazy? yes.
currentState.exitState()
currentState = newState
currentState.enterState()
method onStartButton()
currentState.onStartButton()
method onStartOverButton()
currentState.onStartOverButton()
method onPauseButton()
currentState.onPauseButton()
method onResumeButton()
currentState.onResumeButton()
// This method is called by the "os"
method frameUpdate()
currentState.onFrameUpdate()</code></pre>
<br />
There you have it. PreStartState goes to RunningState. RunningState goes to PausedState and GameOverState. PausedState goes to RunningState, setting the shouldReset flag or not depending on whether the Resume or Start Over button was pushed. GameOverState goes to RunningState.<br />
<br />
Each state sets up the UI according to its context. In the case of this game on iOS, touch events are translated by the OS into the "onButton" events in the view controller, which delegates the events to the GameScene, which in turn delegates them to the current SceneState, which is where the knowledge of what to do is programmed.<br />
<br />
That's the essence of the State design pattern applied to my game architecture. It's not necessarily revolutionary, but it's made a world of difference in the way I track down problems and solve them.Kent Andersonhttp://www.blogger.com/profile/14632601949492972207noreply@blogger.com0