PhotoMemory WP7 App

The Basic Outline of Functionality

  1. Choose the difficulty mode to play in.
  2. Clear default picture values used in development.
  3. Set the board up based on the difficulty mode.
  4. Get personal photos from the phone.
  5. Shuffle photos and select appropriate number based on difficulty mode.
  6. Create pairs and shuffle all cards/photos.
  7. Display unturned cards and any already matched photos.
  8. Choose card and determine matches.
  9. Track number of moves and time.
  10. Display winner once all cards are matched.

PhotoMemory WP7 App

The Game Board (XAML)

The game board is basically a grid with several cells to hold the gradient cards or overturned photos. At the bottom, I keep track of number of moves and time expired.

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <toolkit:WrapPanel Grid.Row="0" x:Name="LayoutPanel" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
            <Image Source="Images/1.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/2.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/3.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/6.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/4.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/1.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/2.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/7.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/8.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/5.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/9.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/3.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/4.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/6.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/7.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/8.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/9.jpg" Width="150" Margin="5"></Image>
            <Image Source="Images/5.jpg" Width="150" Margin="5"></Image>
        </toolkit:WrapPanel>

        <StackPanel Grid.Row="1" Orientation="Horizontal">
            <TextBlock Text="Moves:"></TextBlock>
            <TextBlock x:Name="MovesLabel" Foreground="Yellow" Text="0" Margin="10,0,0,0"></TextBlock>
            <TextBlock Text="Time:" Margin="50,0,0,0"></TextBlock>
            <TextBlock x:Name="TimeLabel" Foreground="Yellow" Text="0 seconds" Margin="10,0,0,0"></TextBlock>
        </StackPanel>
    </Grid>

PhotoMemory WP7 App

Highlighting a Few Areas of Development

Get Photos

A large part of the charm in this simple application is that it uses your own photos as the cards to match. I had pictures of my daughter and some wildlife we had taken while on a hiking trip (as well as some stock images that came with Windows 7) in my phone when I developed this app. Below is code to retrieve the photos from your phone – stored in MediaLibrary.

private List GetPhotos()
        {
            //Get photos from phone
            List photos = new List();
            var library = new MediaLibrary();
            var pics = library.Pictures.ToArray();

            //Shuffle user pics
            pics.Shuffle();

            //Loop through photos and add to game collection
            foreach (var pic in pics)
            {
                BitmapImage bmp = new BitmapImage();
                bmp.SetSource(pic.GetThumbnail());
                photos.Add(bmp);
            }

            //Check if enough photos are available
            if (photos.Count < _tileCount / 2)
            {
                //Setting default images if user does not have enough pictures of their own
                photos.Add(new BitmapImage(new Uri("Images/1.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/2.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/3.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/4.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/5.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/6.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/7.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/8.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/9.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/10.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/11.jpg", UriKind.Relative)));
                photos.Add(new BitmapImage(new Uri("Images/12.jpg", UriKind.Relative)));

                //Shuffle newly added photos as well
                photos.Shuffle();
            }
            
            return photos;
        }

Display Cards

This is the initial generation of the cards (gradients) and their corresponding actions once a user clicks on them.

private void DisplayCards()
        {
            foreach (var card in _collection.Cards)
            {
                var rect = new System.Windows.Shapes.Rectangle()
                {
                    Fill = new LinearGradientBrush(new GradientStopCollection()
                        {
                            new GradientStop()
                            {
                                Color = "#FF003366".ToColor(),
                                Offset = 0                                
                            },
                            new GradientStop()
                            {
                                Color = "#FF208FFF".ToColor(),
                                Offset = 1
                            },
                        }, 45
                    )
                    {
                        MappingMode = BrushMappingMode.RelativeToBoundingBox
                    },
                    Height = _cardHeight,
                    Margin = new Thickness(_margin),
                    Name = string.Format("rect_{0}", card.ID),
                    Stroke = new SolidColorBrush(Colors.White),
                    StrokeThickness = 2,
                    Width = _cardWidth,
                    Visibility = System.Windows.Visibility.Visible
                };

                rect.MouseLeftButtonUp += new MouseButtonEventHandler(ChooseCard);
                LayoutPanel.Children.Add(rect);

                var img = new Image()
                {
                    Height = _cardHeight,
                    HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
                    Margin = new Thickness(_margin),
                    Name = string.Format("card_{0}", card.ID),
                    Source = card.Bitmap,
                    Width = _cardWidth,
                    VerticalAlignment = System.Windows.VerticalAlignment.Stretch,
                    Visibility = System.Windows.Visibility.Collapsed
                };
                
                LayoutPanel.Children.Add(img);
            }
        }

Play a SoundEffect

This code snippet is all over the place on the internet and WP7 forums, but I thought I would include it here for completeness. In PhotoMemory, I play sound effects both in match and non-match scenarios and at the end when all cards are matched for a fun little “triumph”.

//Play failure sound
                            var snd = SoundEffect.FromStream(TitleContainer.OpenStream("Sounds/failed.wav"));
                            snd.Play();

Navigate to Another Page

Again, another simple snippet but this simply shows a way to navigate to another page within WP7 Silverlight.

this.NavigationService.Navigate(new Uri("/Game.xaml?mode=" + mode, UriKind.Relative));

Create and Use a DispatchTimer

Timers are very important in game/mobile development and using a DispatchTimer appears to be the simplest way to do it. Here I create a timer, set some variables such as the duration and what eventhandler to call, and then start the timer. After the timer has expired, the event is raised and I reset the flipped cards.

DispatcherTimer _cardTimer;

//Need to pause for 1 second before flipping back
                            _cardTimer = new DispatcherTimer();
                            _cardTimer.Interval = TimeSpan.FromSeconds(1);
                            _cardTimer.Tick += new EventHandler(FinishStudyingCards);
                            _cardTimer.Start();

void FinishStudyingCards(object sender, EventArgs e)
        {
            //Stop timer
            _cardTimer.Stop();

            //Flip cards back down after finished viewing
            FlipCard(_firstCard, FlipDirection.Down);
            FlipCard(_secondCard, FlipDirection.Down);

            //Allow user to try again
            _firstCard = null;
            _secondCard = null;
            _lock = false;
        }

PhotoMemory WP7 App

Submission

I would like to have a link to the application on Zune, however, because of complications with Microsoft my submission is still pending after more than a month. You can bet I will be writing a post on the submission process as well. Good luck, and have fun developing WP7 apps of your own!

Not All Roses With Azure

Azure

As many of you who occasionally read my blog know, I tend to be an early adopter of most Microsoft technologies. Let me rephrase that, I am an early explorer of most Microsoft technologies; adoption into production generally comes a little later. However, I did try out Azure two months ago with a sandbox web service hosted in the cloud. I hadn’t thought much about it until recently when I received a bill for around eighty dollars. I am an MSDN subscriber, so I figured this must be a mistake (MSDN subscribers are free for 18 months based on limited usage). After discussing the situation with Microsoft I found out the charge was for a second instance I unknowingly had. As it turns out, maintaining a staging and production environment in Azure is actually listed as two instances, even though only one is “production”. I thought staging was internal and therefore not counted as a paid instance, in fact, at the time I was deploying the web service it suggested adding a second instance to maintain the true service level agreement of max up time. This led me to believe I was only running one true instance, and would be therefore covered under the MSDN subscription.

I was wrong, and because it does clearly state it several lines into the lengthy Terms and Conditions document which you agree to by setting up Azure, I had no leg to stand on in refuting the charge. I will admit, I was pretty irritated that as I sat in front of Visual Studio 2010, calling on my WP7, and only moments before spreading Microsoft praises to fellow employees, that there was no understanding or attempt to reconcile… only the cold abruptness of a Microsoft employee telling me how I “clearly signed in full knowledge and agreement”. Formally, the representative filed a request for reimbursement which was promptly denied.

Hopefully, as you excitedly dive into new technology you do so with caution. In Microsoft’s case, I still believe there are no better development tools, no better programming languages, and no worse pricing structures and convolution. I’m hope by reading this, you will at least avoid making the same mistake I did and save yourself a little cash in the process.