More than enough?

When it comes to computer resources I’ve always had this philosiphy that you don’t have enough until you have more than enough. That philosiphy worked out fine for me in the desktop days when the cost in space and power consumption were unnoticable. But now with all my machines (save one) being portables, it makes a big difference.

The laptop I use for work is a Dell Precision M6400. I received the machine with 4 gigs of ram and a measley 160 gig hard drive. The machine had empty memory slots and an empty drive slot. So I bumped the ram up to 12 gigs and added a second (500 gig) drive. ¬†Adding the RAM solved a performance problem I was having; 4 gigs just wasn’t enough for the virtual machines I needed to run and for multiple instances of Visual Studio. But now that I have the RAM the machine runs much hotter. When it comes out of the hibernate state I’ve got to wait for a 12 gig hibernation file to open. If I put it in standby it won’t last a full day before the battery dies. With the extra drive and the RAM together its hard to make this machine last much longer than 20 minutes on battery power. I didn’t think I’d ever say this but I don’t plan to ever max this machins RAM or storage out in light of the tradeoffs of doing so. I’m going to take the original hard drive out.

The lesson I learned from this is that everything has it’s cost, even having more than enough.

New Hardware: Samsung Galaxy Tab

If you follow me on Twitter you’ll know I’ve been looking for a new tablet device. I’ve made my decision. I was originally going to get a Windows tablet. But when I looked at the available tablets I found that the emphasis seems to be on making them smaller and lighter and as a consequence they are lower powered than what I have with longer battery life. I get pretty good battery life already, so there wasn’t a big incentive for me to get a new Windows tablet just yet. Mine is good enough.

For a breif moment I considered the iPad 2 but the new unit looks to be an incremental upgrade from the original. So I’m leaving it alone.

Next on the list was an Android tablet. That’s what I got, a Samsung Galaxy Tab. It’s a nifty little device in it’s own right. It’s small enough to fit in one hand or the back pocket of some jeans (not that I recommend carrying that way, to many pick pockets around) but large enough to make for a good eBook reader. I was also pleased with how consistent it is with other Samsung devices. I’ll be talking more about it (and another mobile operating system!) in the coming weeks.

iOS Firmware Expired?

This always happens to me. After installing a beta/pre release version of iOS I forget to install the final version when it comes out and the firmware expires. Unlike regular firmware updates I’ve never been prompted by iTunes to perform an update of the firmware when a release version is available. It seems that one has to download it and install it manually. When the firmware expires you end up with a device that has all of the notifications and reminders still showing up but the inability to get to the applications producing those notifications or reminders.

Anyway, this happened to me again the other day. I didn’t feel like addressing it then, but now that I’ve got the time I’m downloading the updated firmware (manually) to install. I found the site iPhoneFirmware.com which keeps track of the direct links to all of the iOS device firmware. Makes it much easier to find what I need. On the downside this looks to be a 30 minute download, so I’ll go do some other development for a while.

Tracking High Scores on Windows Phone

Another frequent question I come across in the user forums is related to how some one implements local high scores. The question has come up frequently enough for me to conclude that its to the benifit of the community to have an implementation available that can be used in Silverlight or XNA that is ready to be used with very little setup.

So I’ve made a solution for others to use. By default the component will keep track of up to 10 high scores and will take care of loading and saving itself. If you add a score the component will take care of ensuring the score is in it’s proper place and removing scores that are nolonger one of the top. For persisting the score information I’ve made use of the DataSaver<T> code from a previous blog post. I hope others will find the solution easy to use.

To get started with using the component add a reference to my component to your project. You’ll want to instantiate HighScoreList passing an optional file name that it will use to save score information. It’s possible to keep track of more than one high score list as long as your instances have different file names. One might want to do this if they keep track of scores in different modes separately from each other (Ex: a score list for Difficult mode, a score list for Easy mode, and so on).

HighScoreList _highScoreList = new HighScoreList("MyScores");

Upon instantiation the component will take care of loading any previous high scores without you doing anything more.

To add a score create a new instance of ScoreInfo and populate its PlayerName and Score fields. (There is also a ScoreDate field that automatically gets populated with the current date and time). Then use the AddScore(ScoreInfo) method on the HighScoreList instance to add it to the score list.

ScoreInfo scoreInfo = new ScoreInfo(){PlayerName = "Jack", Score = 1048576};
_highScoreList.AddScore(scoreInfo);

And that’s it, there’s nothing more for you to do. When you make that call the score gets added to the high score list, scores that are no longer in the top 10 (or what ever you set the limit to be) will fall off the list, and the list will automatically be persisted back to IsolatedStorage so that it is available the next time your game runs. Easy, right?

As a test project I’ve created a Silverlight application that allows you to enter new scores and see the behaviour of the component.

Score Keeper Screenshot

The main bits of the source code are below. First the ScoreInfo class which is nothing more than a serializable collection of three properties

/// <summary>
/// ScoreInfo contains information on a single score
/// </summary>
[DataContract]
public class ScoreInfo : INotifyPropertyChanged
{

    // PlayerName - generated from ObservableField snippet - Joel Ivory Johnson
        private string _playerName = String.Empty;

    /// <summary>
    /// The name of the player that made this score
    /// </summary>
        [DataMember]
        public string PlayerName
        {
        get { return _playerName; }
            set
            {
                if (_playerName != value)
                {
                    _playerName = value;
                    OnPropertyChanged("PlayerName");
                }
            }
        }
        //-----

    // Score - generated from ObservableField snippet - Joel Ivory Johnson
        private int _score = 0;

    /// <summary>
    /// The score that the player made
    /// </summary>
        [DataMember]
        public int Score
        {
        get { return _score; }
            set
            {
                if (_score != value)
                {
                    _score = value;
                    OnPropertyChanged("Score");
                }
            }
        }
        //-----

    // ScoreDate - generated from ObservableField snippet - Joel Ivory Johnson
        private DateTime _scoreDate = DateTime.Now;

    /// <summary>
    /// The date and time that the player made the score. If this field is not
    /// assigned a value it will automatically be assigned with the date and time
    /// that the score isntance was created
    /// </summary>
        [DataMember]
        public DateTime ScoreDate
        {
        get { return _scoreDate; }
            set
            {
                if (_scoreDate != value)
                {
                    _scoreDate = value;
                    OnPropertyChanged("ScoreDate");
                }
            }
        }
        //-----
    protected void OnPropertyChanged(String propertyName)
    {
        if(PropertyChanged!=null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    #region INotifyPropertyChanged Members

    public  event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

And then the HighScoreList class, which is a collection class that contains the .

using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.Serialization; namespace J2i.Net.ScoreKeeper { public class HighScoreList : ObservableCollection<ScoreInfo>, INotifyPropertyChanged { static DataSaver<HighScoreList> MyDataSaver = new DataSaver<HighScoreList>(); public HighScoreList() { } public HighScoreList(string fileName):this() { this.ScoreFileName = fileName; HighScoreList temp = MyDataSaver.LoadMyData(fileName); if(temp!=null) { foreach(var item in temp) { Add(item); } } } // MaxScoreCount - generated from ObservableField snippet - Joel Ivory Johnson private int _maxScoreCount = 10; [DataMember] public int MaxScoreCount { get { return _maxScoreCount; } set { if (_maxScoreCount != value) { _maxScoreCount = value; OnPropertyChanged("MaxScoreCount"); } } } //----- // ScoreFileName - generated from ObservableField snippet - Joel Ivory Johnson private string _scoreFileName = "DefaultScores"; [DataMember] public string ScoreFileName { get { return _scoreFileName; } set { if (_scoreFileName != value) { _scoreFileName = value; OnPropertyChanged("ScoreFileName"); } } } //----- // AutoSave - generated from ObservableField snippet - Joel Ivory Johnson private bool _autoSave = true; [DataMember] public bool AutoSave { get { return _autoSave; } set { if (_autoSave != value) { _autoSave = value; OnPropertyChanged("AutoSave"); } } } //----- static int ScoreComparer(ScoreInfo a, ScoreInfo b) { return b.Score - a.Score; } public void SortAndDrop() { List<ScoreInfo> temp = new List<ScoreInfo>(this.Count); foreach(var item in this) { temp.Add(item); } if (temp.Count > MaxScoreCount) { temp.RemoveRange(MaxScoreCount - 1, (temp.Count) - (MaxScoreCount)); } temp.Sort(ScoreComparer); this.Clear(); temp.ForEach((o)=>Add(o)); } public void Save() { if(String.IsNullOrEmpty(ScoreFileName)) throw new ArgumentException("A file name wasn't provided"); MyDataSaver.SaveMyData(this, ScoreFileName); } public void AddScore(ScoreInfo score) { this.Add(score); SortAndDrop(); if(AutoSave) Save(); } protected void OnPropertyChanged(String propertyName) { if(PropertyChanged!=null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; #endregion } }