Archive for August 2009

I wonder if the volume on this TV goes to 11…

Way back in 1984, I came to the enlightening realization that the bulk of my life is spent in the spaces between Spinal Tap moments…

Check out part of this advertisement for a 32″ Emerson LCD HDTV that I received in my e-mail inbox from Tiger Direct:

tigerdirect

I have heard that Dobly audio is awesome. Too bad they weren’t selling a 32′ diagonal television for $299.99, I might have bought it.

My first foray into Code Golf

So I am poking around on Stack Overflow, trying to figure out a way for someone as stupid as me to actually move my reputation up, when I stumble on this post…

Code Golf: Duplicate Character Removal in String (EDIT: Sorry, I had to remove the link as the question was removed by Stack Overflow…)

This seemed simple enough, so here is my VB.NET console application code:

Module Module1
 
    Sub Main()
 
        Dim s As String = "nbHHkRvrXbvkn"
        Console.WriteLine("The original string is: " + s)
        For Each c In s : s = IIf(s.LastIndexOf(c) <> s.IndexOf(c), s.Replace(CStr(c), Nothing), s) : Next
        Console.WriteLine("The modified string is: " + s)
        Console.ReadKey()
 
    End Sub
 
End Module

The line that does all the work is 98 characters, including the spaces that the IDE automagically puts in there for me.

Anyone have any ideas on how to improve it in VB.NET?

Some more Monte Carlo experimentation

Disclaimer: I do not in any way, shape, or form condone gambling. This posting is for entertainment purposes only.

I saw this roulette story on The Daily WTF, and thought that it merited some good ole fashioned brute force computing:

Knocking Me Off The Perch

So here is the VB.NET console application code:

Module Module1
 
    Dim rand As Random
 
    Dim tableResults() As List(Of String)
    Dim theWheel() As String = {"G00", "B2", "R14", "B35", "R23", _
                                "B4", "R16", "B33", "R21", "B6", _
                                "R18", "B31", "R19", "B8", "R12", _
                                "B29", "R25", "B10", "R27", _
                                "G0", "R1", "B13", "R36", "B24", _
                                "R3", "B15", "R34", "B22", "R5", _
                                "B17", "R32", "B20", "R7", "B11", _
                                "R30", "B26", "R9", "B28"}
    Dim wheelCount As Integer
    Dim tableBetAmount() As Integer
    Dim tableBetColor() As String
    Dim tableLostLastTime() As String
 
    Dim tables, stake, bet, matches, goal, attempts As Integer
    Dim cancelBetAfterAWin As String
 
    Function GetASpin() As String
 
        Return theWheel(rand.Next(0, wheelCount))
 
    End Function
 
    Function GetABet(Optional ByVal bigBet As Boolean = False) As Integer
 
        Dim theBet As Integer
 
        If bigBet Then
            theBet = bet * 1.5
        Else
            theBet = bet
        End If
 
        If theBet > stake Then theBet = stake
 
        stake = stake - theBet
 
        Return theBet
 
    End Function
 
    Sub Main()
 
        Dim i, j As Integer
        Dim s As String
 
        rand = New Random
 
        Console.WriteLine("The Perch")
 
        Console.Write("How many tables to watch? (4) ")
        tables = Val(Console.ReadLine)
        If tables = 0 Then tables = 4
        ReDim tableResults(tables)
        ReDim tableBetAmount(tables)
        ReDim tableBetColor(tables)
        ReDim tableLostLastTime(tables)
 
        Dim stakeSave As Integer
 
        Console.Write("How many money to start with? (10) ")
        stakeSave = Val(Console.ReadLine)
        If stakeSave = 0 Then stakeSave = 10
 
        Console.Write("How many money to bet? (10) ")
        bet = Val(Console.ReadLine)
        If bet = 0 Then bet = 10
 
        Console.Write("How many times to hit a color before betting? (4) ")
        matches = Val(Console.ReadLine)
        If matches = 0 Then matches = 4
 
        Console.Write("What is your goal amount? (400) ")
        goal = Val(Console.ReadLine)
        If goal = 0 Then goal = 400
 
        Console.Write("How many times to run the simulation? (1000) ")
        attempts = Val(Console.ReadLine)
        If attempts = 0 Then attempts = 1000
 
        Console.Write("Cancel your bet after a win? (Y) ")
        s = Console.ReadLine.ToUpper.Trim
        If s = "" Then s = "Y"
        cancelBetAfterAWin = (s = "Y")
 
        Dim madeGoal As Integer = 0
 
        wheelCount = theWheel.Count
 
        Dim currAttempt As Integer
        Dim ctr As Integer
        Dim theSpin As String
        Dim spinsSeen As Integer
        Dim result As String
        For currAttempt = 1 To attempts
            ' initialize the table arrays
            stake = stakeSave
            For i = 0 To tables - 1
                tableResults(i) = New List(Of String)
                tableBetAmount(i) = 0
                tableLostLastTime(i) = ""
            Next
            ' initialize the lists
            For j = 1 To matches
                s = String.Format("{0,5}", j.ToString)
                For i = 0 To tables - 1
                    theSpin = GetASpin()
                    tableResults(i).Add(theSpin)
                    s = s + String.Format("{0,5}{1,-5}", "", theSpin)
                Next
                s = s + String.Format("{0,5}", stake.ToString)
                Console.WriteLine(s)
            Next
            spinsSeen = matches
            ' loop
            While (stake > 0 And stake < goal)
                ' place your bets
                For i = 0 To tables - 1
                    If tableLostLastTime(i) <> "" Then
                        ' see if this is the 1st time losing
                        If tableBetAmount(i) = bet Then
                            ' we lost last time out, automatically bet 1.5x
                            tableBetAmount(i) = GetABet(True)
                            tableBetColor(i) = tableLostLastTime(i)
                        Else
                            ' this is the 2nd time losing in a row, so stop betting
                            tableBetAmount(i) = 0
                            tableBetColor(i) = ""
                            tableLostLastTime(i) = ""
                        End If
                    Else
                        ' check to see if the colors match up
                        ctr = 0
                        For Each r In tableResults(i)
                            If Left(r, 1) = "B" Then ctr = ctr + 1
                        Next
                        If ctr = matches Then
                            ' it's coming up black, bet on red
                            tableBetAmount(i) = GetABet()
                            tableBetColor(i) = "R"
                            tableLostLastTime(i) = ""
                        End If
                        ctr = 0
                        For Each r In tableResults(i)
                            If Left(r, 1) = "R" Then ctr = ctr + 1
                        Next
                        If ctr = matches Then
                            ' it's coming up red, bet on black
                            tableBetAmount(i) = GetABet()
                            tableBetColor(i) = "B"
                            tableLostLastTime(i) = ""
                        End If
                    End If
                Next
                ' now spin for each of the tables
                spinsSeen = spinsSeen + 1
                s = String.Format("{0,5}", spinsSeen.ToString)
                For i = 0 To tables - 1
                    ' pop off the oldest spin for each table
                    tableResults(i).RemoveAt(0)
                    ' get a new spin for each table
                    theSpin = GetASpin()
                    tableResults(i).Add(theSpin)
                    If tableBetAmount(i) = 0 Then
                        s = s + String.Format("{0,5}{1,-5}", "", theSpin)
                    Else
                        s = s + String.Format("{0,5}{1,-5}", _
                                              tableBetAmount(i).ToString + _
                                              tableBetColor(i) + ">", theSpin)
                    End If
                    ' check for bets
                    If tableBetColor(i) <> "" Then
                        If Left(tableBetColor(i), 1) = Left(theSpin, 1) Then
                            ' winner
                            stake = stake + tableBetAmount(i) * 2
                            tableLostLastTime(i) = ""
                            If cancelBetAfterAWin Then
                                tableBetAmount(i) = 0
                                tableBetColor(i) = ""
                            End If
                        Else
                            tableLostLastTime(i) = tableBetColor(i)
                        End If
                    End If
                Next
                s = s + String.Format("{0,5}", stake.ToString)
                Console.WriteLine(s)
            End While
            If stake = 0 Then
                result = "Busted"
            Else
                madeGoal = madeGoal + 1
                result = "Won $" + stake.ToString
            End If
            Console.WriteLine("Spins seen: " + spinsSeen.ToString + _
                              "; Result: " + result)
        Next
 
        Console.WriteLine()
        Console.WriteLine("Total simulations: " + attempts.ToString)
        Console.WriteLine("Number of times goal was made: " + madeGoal.ToString)
 
        Console.WriteLine()
        Console.WriteLine("Strike any key to end the program")
        Console.ReadKey()
 
    End Sub
 
End Module

I kind of stumbled into something interesting here. I thought I had coded the application with the rules as described in the posting on The Daily WTF, but the results that I was seeing seemed out of whack to me.  I was running 1,000 simulations and coming up with about 300 times successfully reaching the goal. After I put in the code that breaks down the wheel, bet, and stake status on a spin-by-spin basis, I noticed that I was not cancelling the bet after a win. After fixing it up by adding the code to return to the perch after a winning bet (see the section above with the comment winner), all of a sudden the number of times reaching the goal went from 300 per thousand to 0 to 5 per thousand.

So that you can try it for yourself, I put in an option to control this parameter. From the results, it would appear that if you want to play this way, you should let the bets ride. (But notice that if your bets are riding and you get the required number red or black spins in a row, the bet that is riding could switch colors.)

iPhone Game Development presentation (CIDUG meeting, August 25, 2009)

Mac Liaw gave a presentation on iPhone game development at the Columbus iPhone Developer User Group on August 25, 2009. He mostly talked about how the iPhone is a very different platform to develop games for than the typical video game consoles.

I can sort of understand some of what he was talking about. In my experience developing games for the Atari 2600, a one or two person team can, with a good idea, produce a killer product. (Of course, the iPhone has at minimum 128 megabytes of RAM, while the Atari 2600 had 128 bytes of RAM, which is a topic for another blog post.) I have been kicking around a couple of ideas for games, now all I have to do is learn OpenGL ES. And find an artist who will work for peanuts.

RIP <blink>

I have just learned by accident that the venerable <blink> tag is now unsupported in Internet Explorer 8. Needless to say, I am guessing that my desire to usher the blink tag back into common use (circa 1994 or so) may not get off the ground now.

Brute force solution to a birthday riddle

At one point a long time ago, one of my college professors asked our class how many people it would take to put in a room before the probability that two of the people had the same birthday was greater than or equal to 50 percent, took guesses from a few of us students, and then told us the answer was 12. Coming from a professor, this had to be true.

That answer has not sat well with me lo these many years since my drinking days, so sitting here with nothing else to do, I decided to try a little Monte Carlo problem solving.

Here is my VS 2008 C# console application code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace Birthdays
{
    class Program
    {
        static int numberOfRuns;
        static int[] numberOfPeople;
        static List<int> birthdays;
        static Random rand;
 
        static int GetABirthday()
        {
            int b;
            int maxDays = (rand.Next(1, 5) == 1) ? 367 : 366;
 
            b = rand.Next(1, maxDays);
 
            return b;
        }
 
        static void Main(string[] args)
        {
            rand = new Random();
 
            Console.WriteLine("Birthdays application");
 
            Console.Write("How many times would you like to run a birthday search? ");
            string s = Console.ReadLine();
            numberOfRuns = Convert.ToInt32(s);
 
            numberOfPeople = new int[367];
            for (int i = 0; i < 367; i++)
            {
                numberOfPeople[i] = 0;
            }
 
            Boolean leaveLoop;
            int b;
            int leapDayMatches = 0;
            int leapDayBirthdays = 0;
            int totalBirthdays = 0;
            for (int i = 1; i <= numberOfRuns; i++)
            {
                birthdays = new List<int>();
                leaveLoop = false;
                while (!leaveLoop)
                {
                    b = GetABirthday();
                    totalBirthdays++;
                    if (b == 366)
                    {
                        leapDayBirthdays++;
                    }
                    if (birthdays.Contains(b))
                    {
                        numberOfPeople[birthdays.Count() + 1]++;
                        leaveLoop = true;
                        if (b == 366)
                        {
                            leapDayMatches++;
                        }
                    }
                    else
                    {
                        birthdays.Add(b);
                    }
                }
            }
 
            Console.WriteLine();
            Console.WriteLine("Breakdown of number of people required:");
            int ctr = 0;
            for (int i = 2; i <= 366; i++)
            {
                if (numberOfPeople[i] != 0)
                {
                    ctr += numberOfPeople[i];
                    Console.WriteLine(string.Format("{0} people: {1} ({2:P})", i, 
                                        numberOfPeople[i], ctr * 1.0 / numberOfRuns));
                }
            }
            Console.WriteLine(string.Format("Total birthdays generated: {0}", 
                                                totalBirthdays));
            Console.WriteLine(string.Format("Leap day birthdays: {0}", 
                                                leapDayBirthdays));
            Console.WriteLine(string.Format("Leap day matches: {0}", 
                                                leapDayMatches));
            Console.WriteLine();
            Console.WriteLine("Strike any key to end the program");
            Console.ReadKey();
        }
    }
}

The answer yielded by the above code is 23, as any meaningful sample size plugged into the program above will demonstrate.

Of course, we did not have the internet back then, but now a quick Google search yields plenty of discussion of the theory and math behind the puzzle. If you are interested, click this link.

Sentinel.v3.5Client error in ClickOnce app install

We have had bad experiences in the past deploying desktop applications based on the .Net Framework version 3.5, largely due to the huge payload required on the end user’s system to run our relatively simple application.

When SP1 of Visual Studio 2008 was announced to have an option to trim down the bits to be downloaded, we were naturally excited to put it into place on our newest product. In a VB.NET application, this option is called “Client-only Framework subset”, it is on the Advanced Compiler Settings window shown by clicking Advance Compile Options… on the Compile tab of My Project, and in a C#.NET application, the Client-only Framework subset check box is right under Target Framework on the Application tab shown by right clicking on the project file and selecting Properties.

Excited until, of course, we tried for the very first time to run the ClickOnce installer for our product on a clean Windows XP SP3 virtual machine and received the following error:

Unable to install or run the application. The application requires that
assembly Sentinel.v3.5Client Version 3.5.0.0 be installed in the Global
Assembly Cache (GAC) first.

I did some quick investigating and found that, for the most part, the suggested solution was to turn off the client only subset option. Bugger!

This did not sit well with me, so I did a little more digging. I did not think that there were any kind of items added to the code that would require namespaces not included in the client only framework, so I went through all of the property pages for the project.

On the Publish tab, I clicked on Preqrequisites, and noticed that my application was including prerequisites for Windows Installer 3.1, .NET Framework 3.5, and SQL Server Compact 3.5. On a lark, I turned off the prerequisite for .NET Framework 3.5 and turned on the prerequisite for .NET Framework 3.5 SP1, built the ClickOnce installer, and voila! It is now working like a champ.

How to Build an iPhone App that Doesn’t Suck

A guest lecture for the Stanford University iPhone Application Programming class called “How to Build an iPhone App that Doesn’t Suck!” was hosted by Steve Marmon on May 8, 2009. Steve covered some of his ideas on, shockingly enough, the guidelines of designing a good iPhone application.

One thing he pointed out that I found interesting was the process of UI layout. He pointed out that an interface should be designed 10 times instead of just once, the theory being that by the time you reach the 10th design of the interface, you have fleshed out all of the ideas for the interface.

Stanford iPhone App Programming lecture 15

Lecture 15 from the Stanford University iPhone Application Programming class was hosted by Justin Santamaria from Apple. Justin covered the photo picker, Core Location, and accelerometer topics during his presentation, none of which were particularly pertinent to my current projects.

Unfortunately, Justin did not show live demos of these components, which is understandable considering the fact that the simulator has limited support for these components. The code snippets in the slides should be sufficient for getting these things up and running.

At the end of the presentation, Justin also covered some hints on maximizing battery life on the platform.

Subversion operations on Visual Studio 2008 solution take forever

Well, not literally, just a very very long time.

Our Subversion source repository has grown to be quite sizable over the past couple of years. We have always used TortoiseSVN and VisualSVN to tame this beast, and it has usually not let us down.

However, a while back, something bad happened to a VS 2008 solution in the repository. When launching the solution, the refresh of the version control status indicators next to the files in the solution take a very long time to appear and/or refresh, and any kind of operation done on the solution file (such as update, repo browser, etc.) goes back to the root of the whole repository instead of working with the folder where the solution file lives.

Strangely enough, operations done on any of the components inside of the solution does the right thing. In other words, if I update a project in the solution from Visual Studio, it works lightning fast.

I finally got tired of wasting 2 to 10 minutes per operation on the solution and studied this problem by comparing this malfunctioning solution with one that appears to do what you would expect it to. One thing I noticed right away was that the speedy project contained the following lines at the bottom of the solution file (opened in your favorite text editor, of course):

    GlobalSection(ExtensibilityGlobals) = postSolution
        VisualSVNWorkingCopyRoot =
    EndGlobalSection
    GlobalSection(SubversionScc) = preSolution
        Svn-Managed = True
        Manager = AnkhSVN - Subversion Support for Visual Studio
    EndGlobalSection

I added these lines to the bottom of my sluggish solution file and re-opened it, and I now predict explosive advances in my productivity. Or at least more time for tweeting.

Now all I need to do is figure out how to remove these disabled “DSL Tools”, “Text Transformation”, “Workflow”, and “ANTS 4” menu items from my VS 2008. Anyone have any ideas? (EDIT: John Boker has found the solution, see the comments.)

EDIT, PART DEUX: I tried this fix on a different existing Visual Studio project on 9/23/2009, and of course it didn’t work. I studied the problem a bit more, and found that if I added the GlobalSection(SubversionSCC) section that I have already inserted into the code block above, it did the trick for me. (Even though I am not actually using AnkhSVN, I am using TortoiseSVN, so I am guessing the Manager line is not important. However, I have left it in there for completeness.)