DVD Unlimited - Thanks, but no thanks

We have just ended a three month free trial with DVD Unlimited, and we will not continue with their service.  We got the trial as part of buying our TV, so it did not hurt us to try.

Things I liked:

  • DVDs arrived in the mail
  • You could return the DVDs in the mail
  • You could keep a movie for as long as you want, this would work well for games (not that we got any of the games from our request list)
  • We got to see lots of foreign movies, as there seems to be no queue on these

Things that I did not like:

  • I’m not sure we ever got any DVD in the top ten of our 20+ DVD selection list
  • I’m not sure any of the DVDs we received would be ‘new release’ at the local video shop
  • Three of the first four DVDs didn’t play error free.  While they have a good swap program, it was a disappointing start, especially when the movies were not worth replacing, but only ‘watch it because we have it‘ quality
  • The movie suggestion system never had any results
  • When rating movies, the “0 1 2 3 4 5” text link based ratings where hard to read, and also hard to gauge meaning of the levels.  I really like how Amazon use bright stars, with text describing the meaning of that level eg “It’s ok”, “I like it”, “I love it”.  This allows you to know others will interpret those statements the same
  • They have lots of TV series, which is good if you want that, but I don’t and there’s no way to say “stop showing me all these freaking TV shows”, well there is a “not interested” button, but that seemed to have zero effect
  • When rating lists of DVDs I found it highly disturbing how the next item is auto focused/scrolled.  Once you learned what was happening, it sort of worked, but only if you had not scrolled the window, otherwise its auto jump feature moved incorrectly
  • It was very hard to find the details of our plan, and the options on how to handle our monthly limit where not in line with our trial limit. The limiting feature told us our plan was 10, but only gave the option of “4 movies a month(your limit” which it wasn’t
  • I didn’t like their terms and conditions, one of which includes that you are not allowed to link to them. Fine no Google juice for you!
  • Over the last week, after logging on to the site, the site shows this prompt:
    NAME_OF_MOVIE was awesome, don’t you think?
    Your Rating: 0 1 2 3 4 5
    Write Review

    No, I already told you it was crap (1), so stop asking.  But there it is again
  • There is a pressure to watch DVDs when they arrive so you get your $$ worth.  I guess this happens if you rent too many movies from a rental shop also…

So over all, $28 x 3 months for 20 DVDs which I’m sure I could rent for $1 each, plus fast post each way equals

Cost: $84 > Value: $60, so even if I’m generous and cost each DVD at $2 each + pp ($2), that brings the value to $80, which still below cost.

Mac Widget - Calc Points

Calc Points Widget
Calc Points Widget

I have now made my Mac Calc Points widget available.

The widget is used for calculating the ‘points’ for food, based on the kilojoules, saturated fat, and serving size on the food packet.

You can choose kilo-calories or kilo-joules, measurements ‘per 100g’ or ‘per serving’, and the calculations based on constant numbers that are used in New Zealand or those suggested on Wikipedia.

To change the options, just click on the i in the bottom right corner.

Sicko - Michael Moore

Michaela and I have just finished watching Sicko, fantastic movie/documentry, that I’d been keen to watch for a while. Nic has an even better write-up so just read his (in full).

It has made me hesitant about working in the US, it seems illogical to go somewhere so messed up.  In fact I feel not so bullet-proof about my pending trip there.

I guess I’ll take it a day at atime, and hope for good luck, just like 30 million Americans.

Oracle's Lag in MS SQL Server 2005

I am currently porting our new database from Oracle 10g to MS SQL Server 2005, and I have it all done except the views that use the Oracle LAG and LEAD functions (non-ANSI).

What these functions provide (for the MS SQL camp) is the ability to get the next or previous rows when sorted. In my case I have a value that is the ‘volume change since the start’ at time intervals, and I want the relative change between each interval.

So PL/SQL of the view is:

SELECT t.*,
    t.volume - LAG(volume) OVER (PARTITION BY group_number  ORDER BY timestamp) AS volume_change
FROM volume_table t;

The partition clause splits the data into different buckets, then each bucket is sorted, with all results returned.

Asking on the NZ .Net User Group mailing list I got a pointer to this MS feedback page, but the solution presented there gives me an error “Incorrect syntax near ‘ROWS’.“ when I run this query against SQL 2K5

SELECT MIN(volume) OVER(PARTITION BY group_number  ORDER BY timestamp
    ROWS BETWEEN 1 PRECEDING  AND 1 PRECEDING) change
FROM volume_table; GO

I had a side point showing why I wanted to avoid sub-select, as the performance of a different query had an orders of magnitude improvement from changing to using a LAG function, yet that same sub-select query runs just as fast as the “improved” Oracle statement in MS SQL Server, so I’ll just stick to the main topic, and post about that another day…

Chris recently showed how to use Common Table Expressions (CTE) (sort of auto-magic temp table) to find the first entries for a day, which is very close to what I was want, but the filtering is hard coded.  I could not see how to make it dynamic, so I used the idea, and started massaging the concept, till I finally got what I wanted.

Conceptually the Oracle solution could be done using cursors under the hood to provide the rolling previous (LAG) rows, where-as here I’m doing many look-ups but the table is not getting re-created as in the nested select method.

So my code is as follows:

WITH Rows( vol_diff, time, rn, gn ) AS (
  SELECT v.volume,
    v.timestamp,
    Row_Number() OVER (PARTITION BY group_number ORDER BY timestamp),
    group_number
  FROM volume_table v
), PrevRows( timestamp, prev_vol, group_number) AS (
  SELECT a.time, b.vol_diff, a.gn
  FROM Rows a
  LEFT JOIN Rows b
    ON a.rn = b.rn + 1  AND a.gn = b.gn
)
SELECT v.*, v.volume - p.prev_vol as volume_change
FROM volume_table v
LEFT JOIN PrevRows p
    ON v.timestamp = p.timestamp
    AND v.group_number = p.group_number; GO

So I use two CTE tables, one to partition and sort the data, the second to do a lag based join, then I can select the lagged based data, by matching the time and group to the current entry.It works a treat, and I will do some performance testing tomorrow once my production data has finished loading into my db.

After the results of the not discussed query I expect that the sub-select will be just as performant.

Best Song Ever: Orbital - Kein Trink Wasser

Man I have loved this song for ages, and today I have been listening to only my ‘five-star’ songs, and after having listened to the list at least twice, it is still the undisputed ‘Best Song Ever’.

It has a fantastic driving piano build up, with clever layers of piano.  Two fifths through it drops into some fantastic bass that then builds its own layers, and at the half-way mark re-combine with the piano layers, weaving and blending till the end.

Gives me tingles every time I listen, and I’m compelled to jiggle my feet in time, for which Matt once threw a book at me…. but he’s gone!

There is a live performance on You Tube, but the sound quality is missing the feel of it, also the visuals distract from the journey.

How I got into programming (meme)

It’s been funny watching this meme slowly traverse my blog roll, and now I’ve been tagged. Cheers Chris

How old were you when you first started programming?

Most likely 8 or 9.

How did you get started in programming?

Roland goes digging
Roland goes digging

My father brought an Amstrad CPC464 (green screen) to do his thesis on.  Between ‘Roland goes digging’ and other games, making my own stuff became a fascination.

What was your first language?

Basic and Logo, lots of exploration with trivial stuff, also lots of entering game listings from Amstrad Computer User, in basic and/or hex.  I can’t believe the hours we spent reading in hex, and entering line by line, double/triple checking each one.  We never did get Splat! working….

What was your first real program you wrote?

Real? they really were all real, just not really useful.

Um, outside the demo-scene type graphics stuff, mode-x, ray tracing, phong shading stuff, I’m not sure when I wrote anything of outside value.  During high-school I hacked lots of games, to run, or fiddled the save games to give me lots of money, or better stats.  I wrote a telnet proxy at Uni to get free mudding

The first paid ‘programming’ I did was hacking the POS system at the pizza shop I worked at to change the inventory list, to avoid paying the developers consulting rates…

What languages have you used since you started programming?

To make new stuff:

Loco Basic, Logo, MS QuickBasic, Turbo C, Assembly (x86, 68k, PowerPC, custom), C, Quake-C, Shell/Scripting (Bash, Expect), C++, C#, Erlang

To alter/edit/fix:

Cobal, Fortran 77, VB6, VB.Net, Delphi

What was your first professional programming gig?

Software tester at Teltrend NZ(became Allied Telesyn Research which became Allied Telesis Labs), writing test tools, and automated testing in C, Bash, and Expect. Lots of braking other people’s stuff.

If you knew then what you know now, would you have started programming?

Heck yes, I love making things work, and I still have yet to create my own self adapting machine (aka Skynet/Terminator)

If there is one thing you learned along the way that you would tell new developers, what would it be?

Learn to break stuff as well as build happy day software. Learn how things can/will go wrong, and at least say ‘we not covering that case’, rather than just be ignorant.

What’s the most fun you’ve ever had… programming?

Hmm,

Mud Mapping Client @ Uni: I spent a few too many hours playing on TFE and wrote a ncursors based mapping client and telnet proxy to help playing.  I loved those large Sparc 5 screens.

3D data stuff @ Motion-Art: Also another fantastic time here, writing scripts for 3D Studio Max 2.0. We had a golf course exported from Autocad, and it took over an 1 hour to load the dxf file. We could not even mesh it with 128MB of RAM and 2x1GB swap disks and 48 hours time. So I wrote scripts to reduce the dataset to manageable volumes. Lots of fun, just making stuff work.

Curse Azure Bonds port @ now: This is a labour of love, and I’ve been working on it (in one form of another) for over 9 years.  I call it my knitting project, because I just pick it up, do a little and put it down for latter.  It is so satisfying un-weaving how the original game was built and worked.

I tag Matthew Owens-Smith, Shannon Smith, and Conor Boyd

Memoizing with ETS

In my 3n + 1 post rickhg12hs suggested I use the erlang ets module to memoize my solution.

To time the code before and after changes I found this little gem from David King

-module(timer).
-export([timer/3]).

timer(Mod,Fun,Args) ->
    Start=erlang:now(),
    Result = apply(Mod,Fun,Args),
    Stop=erlang:now(),
    io:format("~p~n", [time_diff(Start,Stop)]),
    Result.

time_diff({A1,A2,A3}, {B1,B2,B3}) ->
    (B1 - A1) * 1000000 + (B2 - A2) + (B3 - A3) / 1000000.0 .

Using this I found that my old code for 1 -> 1,000,000 took 13.1 seconds..

I upgraded the code to use an ets ordered_set like this:

-module(ets3np1).
-export([tnpo_range/2]).tnpo_range(Min, Max) ->
    Tib = ets:new(x, [ordered_set,private]),
    Result = lists:max([tnpo(Tib, X) || X <- lists:seq(Min, Max) ]),
    ets:delete(Tib),
    Result.

tnpo(_, 1) -> 1;
tnpo(Tib, Num) ->
    tnpo( ets:lookup(Tib, Num), Tib, Num ).
tnpo([], Tib, Num) ->
    case( Num rem 2) of
        1 -> NewNum = (Num * 3) + 1;
        0 -> NewNum = Num div 2
    end,
    Val = tnpo(Tib, NewNum) + 1,
    ets:insert(Tib, { Num, Val }),
    Val;

tnpo([{_,Val}], _, _) -> Val.

which ran at the improved time off 8.8 seconds.

Initially I had forgotten to delete the ets tables so my lappy started grinding after a few iterations as it thrashed the swap.  Ah the joys of learning a feature via the API reference…

One nagging question I had was the speed of using the lists:seq and the list comprehension.  So I swapped to an explicit loop via parameter matching, which ran slightly faster.  Luckily I realised the code smell: the original could be mapped across many processors with little effort, but the latter was hard coded looping.

So here’s that bad code:

tnpo_range_b(Min, Max) ->
    Tib = ets:new(x, [ordered_set, private]),
    Result = tnpo_range_b(Tib, Min, Max, 0),
    ets:delete(Tib),
    Result.
tnpo_range_b(Tib, Num, Max, Best ) when Num =< Max ->
    Result = tnpo(Tib, Num),
    tnpo_range_b(Tib, Num + 1, Max, lists:max([Result,Best]));
tnpo_range_b(_, _, _, Best ) -> Best.

After that I then wondered what the difference between set (hash-table) and ordered_set (b-tree), so I altered my code again.

So the final results are (averaged over three runs):

Input 1 -> 1,000,000
Original:  13.1 seconds
Ordered_Set:  8.8 seconds
Bad-Code: 8.7 seconds
Set:  8.9 seconds

Input 1 -> 2,000,000
Original:  27.8 seconds
Ordered_Set:  18.6 seconds
Bad-Code: 18.2 seconds
Set:  18.8 seconds

Of funny coincidence, the day after doing the coding, I read the ets section in Programming Erlang, which turns out to cover the topic nicely.

Curse of the Azure Bonds - build 1.0.13

The game can now be completed.  I finished my dungeon crawl last night, with the help of the magic God’s Intervene cheat.

Here are the changes in build 1.0.13:

  • Fixed issues stopping the end-game sequence from working, so the game is now winnable
  • Fixed the crash that happened when sword of Frost Brand, or Flame Brand is readied
  • Fixed the way text wrapping happens, so the end game text is displayed the same as in the DOS version
  • Fixed the animation picture code, so end-game animations are drawn correctly
  • Fixed a small 3D view rendering problem with the far distance wall
  • Cheat settings are now saved in your user profile, so you don’t have to keep turning them on each time you start the game
  • Time now passes correctly when resting or searching and after combat
  • Altered the Area Map to now overlay the arrow icon cleanly
  • Added new cheat “Improved Area Map” that shows doors in blue, allowing for walking around a dungeon completely from the Area Map

I have been working on getting the sound sub-system ground-work done, but functioning sound is still a long way off.

The Trip - 110103 in Erlang

The third of my Programming Challenges solutions is for The Trip 110103

This problem is great for making you think.  Key points are; you cannot have half cents, and you have to find the minimum money needed to balance costs to within one cent.

Here is my solution in Erlang:

-module(thetrip).
-export([thetrip/1, test/0]).

average([H|T], Sum, Len) ->
  average(T, Sum+H, Len+1);
average([], Sum, Len) ->
  [Sum div Len , (Sum rem Len) > 0].

diff([H|T], Sum, Avg, Odd) when H > Avg, Odd ->
  diff(T, Sum + H - Avg - 1, Avg, Odd);
diff([H|T], Sum, Avg, Odd) when H > Avg ->
  diff(T, Sum + H - Avg, Avg, Odd);
diff([_|T], Sum, Avg, Odd) ->
  diff(T, Sum, Avg, Odd);
diff([], Sum, _, _ ) ->
  Sum.

thetrip(List) ->
  Scaledup = [ trunc( X * 100 ) || X <- List],
  [Avg, Odd] = average(Scaledup, 0, 0),
  Diff = diff(Scaledup, 0, Avg, Odd ) / 100,
  io:format("$~.2f~n",[Diff]),
  Diff.

test() ->
  10.00 = thetrip([10.00, 20.00, 30.00]),
  11.99 = thetrip([15.00, 15.01, 3.00, 3.01]),
  14.39 = thetrip([15.00, 15.01, 3.00, 3.01, 3.01]),
  19.57 = thetrip([15.00, 15.01, 3.00, 3.01, 3.02, 3.03, 3.04, 3.05, 3.06, 3.07, 3.08]),
  true.

I really enjoyed getting my head around guard sequences and using local variables to space the code out.