├── Licence.txt
├── README.md
├── SudokuMaster.sln
├── SudokuMaster
├── App.xaml
├── App.xaml.cs
├── ApplicationIcon.png
├── Bin
│ └── Release
│ │ └── SudokuMaster.xap
├── BoardModel.cs
├── BoardValue.cs
├── Cell.xaml
├── Cell.xaml.cs
├── GameOver.xaml
├── GameOver.xaml.cs
├── Gamelogic.cs
├── HighscoreItem.cs
├── HighscoresPage.xaml
├── HighscoresPage.xaml.cs
├── MainPage.xaml
├── MainPage.xaml.cs
├── MemoryDiagnosticsControl.cs
├── NumberSelection.xaml
├── NumberSelection.xaml.cs
├── Point.cs
├── Properties
│ ├── AppManifest.xml
│ ├── AssemblyInfo.cs
│ └── WMAppManifest.xml
├── SoundHelper.cs
├── SplashScreenImage.jpg
├── SudokuMaster.csproj
├── WaitNote.xaml
├── WaitNote.xaml.cs
├── background.png
├── gfx
│ ├── back.png
│ ├── background.png
│ ├── brownGridItem.png
│ ├── darkGridItem.png
│ ├── empty.png
│ ├── highscores.png
│ ├── lightGridItem.png
│ ├── logo.png
│ ├── move.png
│ ├── newgame.png
│ ├── statistic.png
│ ├── time.png
│ └── waitNote.png
└── sounds
│ ├── 60443__jobro__tada1.wav
│ ├── 7040__yawfle__050816_chair_04.wav
│ └── 7043__yawfle__050816_chair_07.wav
└── doc
├── ClassDiagram.png
├── ScreenShot.png
└── porting
├── Qt_Highscores.2.png
├── Qt_Highscores.png
├── Qt_Landscape.png
├── Qt_MainView.2.png
├── Qt_MainView.png
├── Qt_Numberpad.2.png
├── Qt_Numberpad.png
├── Qt_Options.png
├── Qt_Waitnote.2.png
├── Qt_Waitnote.png
├── SL_Highscores.2.png
├── SL_Landscape.png
├── SL_MainPage.png
├── SL_Numberpad.png
├── SL_Splashscreen.png
└── SL_Waitnote.png
/Licence.txt:
--------------------------------------------------------------------------------
1 | Copyright © 2011-2014 Microsoft Mobile Oy. All rights reserved.
2 |
3 | Microsoft is a registered trademark of Microsoft Corporation. Nokia and HERE are
4 | trademarks and/or registered trademarks of Nokia Corporation. Other product and
5 | company names mentioned herein may be trademarks or trade names of their
6 | respective owners.
7 |
8 |
9 | License
10 |
11 | Subject to the conditions below, you may use, copy, modify and/or merge copies
12 | of this software and associated content and documentation files (the ”Software”)
13 | to test, develop, publish, distribute, sub-license and/or sell new software
14 | derived from or incorporating the Software, solely in connection with Microsoft
15 | Mobile devices (however branded). Some of the documentation, content and/or
16 | software may be licensed under open source software or other licenses. To the
17 | extent such documentation, content and/or software are included, those licenses
18 | and/or other terms and conditions shall apply in addition and/or instead of this
19 | notice. The exact terms of the licenses, disclaimers, acknowledgements and
20 | notices are reproduced in the materials provided, or in other obvious locations.
21 | No other license to any other intellectual property rights is granted herein.
22 |
23 | This file, unmodified, shall be included with all copies or substantial portions
24 | of the Software that are distributed in source code form.
25 |
26 | The Software cannot constitute the primary value of any new software derived
27 | from or incorporating the Software.
28 |
29 | Any person dealing with the Software shall not misrepresent the source of the
30 | Software.
31 |
32 |
33 | Disclaimer
34 |
35 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
37 | FOR A PARTICULAR PURPOSE, QUALITY AND NONINFRINGEMENT. IN NO EVENT SHALL THE
38 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES (INCLUDING,
39 | WITHOUT LIMITATION, DIRECT, SPECIAL, INDIRECT, PUNITIVE, CONSEQUENTIAL,
40 | EXEMPLARY AND/ OR INCIDENTAL DAMAGES) OR OTHER LIABILITY, WHETHER IN AN ACTION
41 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
42 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
43 |
44 | Microsoft Mobile Oy retains the right to make changes to this document at any
45 | time, without notice.
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Sudokumaster Silverlight Example
2 | ================================
3 |
4 | Sudokumaster is a Sudoku mobile game developed with Silverlight for Windows
5 | Phone devices. The game is a logic-based, combinatorial number-placement
6 | puzzle with nine 3x3 grids each containing all the digits from 1 to 9. In the
7 | beginning only some of the numbers are placed in the grids and the player
8 | needs to figure out the correct positions for the missing numbers.
9 |
10 | The application is a rewrite of the Qt Sudokumaster application for Symbian
11 | and Maemo devices.
12 |
13 |
14 | PREREQUISITIES
15 | -------------------------------------------------------------------------------
16 |
17 | - C# basics
18 | - Development environment 'Microsoft Visual Studio 2010 Express for Windows
19 | Phone'
20 |
21 | LINKS
22 | -------------------------------------------------------------------------------
23 |
24 | Getting Started Guide:
25 | http://create.msdn.com/en-us/home/getting_started
26 |
27 | Learn About Windows Phone 7 Development:
28 | http://msdn.microsoft.com/fi-fi/ff380145
29 |
30 | App Hub, develop for Windows Phone:
31 | http://create.msdn.com
32 |
33 |
34 | IMPORTANT FILES
35 | -------------------------------------------------------------------------------
36 |
37 | MainPage.xaml/.cs: Main page of the application, the game view.
38 |
39 | HighscoresPage.xaml/.cs: High scores (or top times) page, contains a list of
40 | 20 best times/moves.
41 |
42 | Gamelogic.cs: Game board generation, logic for the game.
43 |
44 | Cell.xaml/.cs: Represents a single cell on the game board.
45 |
46 | GameOver.xaml: Dialog which is shown when the game ends.
47 |
48 | NumberSelection.xaml/.cs: Dialog which is shown when the player taps on a cell.
49 |
50 | WaitNote.xaml/.cs: Spinning circle animation which is displayed while
51 | generating a new puzzle.
52 |
53 |
54 | KNOWN ISSUES
55 | -------------------------------------------------------------------------------
56 |
57 | None.
58 |
59 |
60 | BUILD & INSTALLATION INSTRUCTIONS
61 | -------------------------------------------------------------------------------
62 |
63 | **Preparations**
64 |
65 |
66 | Make sure you have the following installed:
67 | * Windows 7
68 | * Microsoft Visual Studio 2010 Express for Windows Phone
69 | * The Windows Phone Software Development Kit (SDK) 7.1
70 | http://go.microsoft.com/?linkid=9772716
71 |
72 |
73 | **Build on Microsoft Visual Studio**
74 |
75 | Please refer to:
76 | http://msdn.microsoft.com/en-us/library/ff928362.aspx
77 |
78 |
79 | **Deploy to Windows Phone 7**
80 |
81 | Please refer to:
82 | http://msdn.microsoft.com/en-us/library/gg588378.aspx
83 |
84 |
85 |
86 | RUNNING THE APPLICATION
87 | -------------------------------------------------------------------------------
88 |
89 | An empty Sudoku game board is displayed after the application is started. The
90 | menu at the bottom of the screen contains two buttons, New Game and Highscores.
91 | Press the New Game button to start the game. Tap on an empty cell on the grid,
92 | and a dialog pops up where you can select a number or clear the value of the
93 | cell. Only the empty cells and the cells the player has set earlier (cells
94 | with white numbers) can be manipulated. The objective of the game is to fill
95 | the board with numbers between 1 and 9 according to the following guidelines:
96 |
97 | - A number can appear only once in each row
98 | - A number can appear only once in each column
99 | - A number can appear only once in each region
100 |
101 | A region is 3x3 squares, and the board is divided into 3x3 regions identified
102 | by lighter and darker cells.
103 |
104 | Below the board are three icons and numbers besides them; number of moves the
105 | player has made, remaining empty cells, and game time.
106 |
107 | The game ends when all the cells are filled. If a new high score was achieved,
108 | the player's name is queried.
109 |
110 |
111 | COMPATIBILITY
112 | -------------------------------------------------------------------------------
113 |
114 | - Windows Phone 7
115 |
116 | Tested on:
117 | - Nokia Lumia 800
118 | - Nokia Lumia 900
119 |
120 | Developed with:
121 | - Microsoft Visual Studio 2010 Express for Windows Phone
122 |
123 |
124 | LICENCE
125 | -------------------------------------------------------------------------------
126 |
127 | You can find license details in Licence.txt file provided with this project
128 | or online at
129 | https://github.com/Microsoft/sudokumaster-wp/blob/master/Licence.txt
130 |
131 |
132 | CHANGE HISTORY
133 | -------------------------------------------------------------------------------
134 |
135 | 1.2 Code level improvements
136 | 1.1 Code quality improvements
137 | 1.0 First release
138 |
--------------------------------------------------------------------------------
/SudokuMaster.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 11.00
3 | # Visual Studio 2010 Express for Windows Phone
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SudokuMaster", "SudokuMaster\SudokuMaster.csproj", "{8062FAC1-CF4C-4056-B3B7-5DFFE9EEF4B5}"
5 | EndProject
6 | Global
7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 | Debug|Any CPU = Debug|Any CPU
9 | Release|Any CPU = Release|Any CPU
10 | EndGlobalSection
11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
12 | {8062FAC1-CF4C-4056-B3B7-5DFFE9EEF4B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
13 | {8062FAC1-CF4C-4056-B3B7-5DFFE9EEF4B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
14 | {8062FAC1-CF4C-4056-B3B7-5DFFE9EEF4B5}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
15 | {8062FAC1-CF4C-4056-B3B7-5DFFE9EEF4B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
16 | {8062FAC1-CF4C-4056-B3B7-5DFFE9EEF4B5}.Release|Any CPU.Build.0 = Release|Any CPU
17 | {8062FAC1-CF4C-4056-B3B7-5DFFE9EEF4B5}.Release|Any CPU.Deploy.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/SudokuMaster/App.xaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
--------------------------------------------------------------------------------
/SudokuMaster/App.xaml.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2014 Microsoft Mobile.
3 | */
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Net;
9 | using System.Windows;
10 | using System.Windows.Controls;
11 | using System.Windows.Documents;
12 | using System.Windows.Input;
13 | using System.Windows.Media;
14 | using System.Windows.Media.Animation;
15 | using System.Windows.Navigation;
16 | using System.Windows.Shapes;
17 | using Microsoft.Phone.Controls;
18 | using Microsoft.Phone.Shell;
19 | using SudokuMaster.Debug;
20 |
21 | namespace SudokuMaster
22 | {
23 | public partial class App : Application
24 | {
25 | ///
26 | /// Provides easy access to the root frame of the Phone Application.
27 | ///
28 | /// The root frame of the Phone Application.
29 | public PhoneApplicationFrame RootFrame { get; private set; }
30 |
31 | ///
32 | /// Constructor for the Application object.
33 | ///
34 | public App()
35 | {
36 | // Global handler for uncaught exceptions.
37 | UnhandledException += Application_UnhandledException;
38 |
39 | // Show graphics profiling information while debugging.
40 | if (System.Diagnostics.Debugger.IsAttached)
41 | {
42 | // Display the current frame rate counters.
43 | Application.Current.Host.Settings.EnableFrameRateCounter = true;
44 |
45 | // Show the areas of the app that are being redrawn in each frame.
46 | // Application.Current.Host.Settings.EnableRedrawRegions = true;
47 |
48 | // Enable non-production analysis visualization mode,
49 | // which shows areas of a page that are being GPU accelerated with a colored overlay.
50 | // Application.Current.Host.Settings.EnableCacheVisualization = true;
51 | }
52 |
53 | // Standard Silverlight initialization
54 | InitializeComponent();
55 |
56 | // Phone-specific initialization
57 | InitializePhoneApplication();
58 |
59 | #if (DEBUG)
60 | // in debug mode display memory consuption
61 | MemoryDiagnosticsControl.Start(false);
62 | #endif
63 | }
64 |
65 | // Code to execute when the application is launching (eg, from Start)
66 | // This code will not execute when the application is reactivated
67 | private void Application_Launching(object sender, LaunchingEventArgs e)
68 | {
69 | Highscores.Load();
70 | }
71 |
72 | // Code to execute when the application is activated (brought to foreground)
73 | // This code will not execute when the application is first launched
74 | private void Application_Activated(object sender, ActivatedEventArgs e)
75 | {
76 | Highscores.Load();
77 | }
78 |
79 | // Code to execute when the application is deactivated (sent to background)
80 | // This code will not execute when the application is closing
81 | private void Application_Deactivated(object sender, DeactivatedEventArgs e)
82 | {
83 | }
84 |
85 | // Code to execute when the application is closing (eg, user hit Back)
86 | // This code will not execute when the application is deactivated
87 | private void Application_Closing(object sender, ClosingEventArgs e)
88 | {
89 | }
90 |
91 | // Code to execute if a navigation fails
92 | private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
93 | {
94 | if (System.Diagnostics.Debugger.IsAttached)
95 | {
96 | // A navigation has failed; break into the debugger
97 | System.Diagnostics.Debugger.Break();
98 | }
99 | }
100 |
101 | // Code to execute on Unhandled Exceptions
102 | private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
103 | {
104 | if (System.Diagnostics.Debugger.IsAttached)
105 | {
106 | // An unhandled exception has occurred; break into the debugger
107 | System.Diagnostics.Debugger.Break();
108 | }
109 | }
110 |
111 | #region Phone application initialization
112 |
113 | // Avoid double-initialization
114 | private bool phoneApplicationInitialized = false;
115 |
116 | // Do not add any additional code to this method
117 | private void InitializePhoneApplication()
118 | {
119 | if (phoneApplicationInitialized)
120 | return;
121 |
122 | // Create the frame but don't set it as RootVisual yet; this allows the splash
123 | // screen to remain active until the application is ready to render.
124 | RootFrame = new PhoneApplicationFrame();
125 | RootFrame.Navigated += CompleteInitializePhoneApplication;
126 |
127 | // Handle navigation failures
128 | RootFrame.NavigationFailed += RootFrame_NavigationFailed;
129 |
130 | // Ensure we don't initialize again
131 | phoneApplicationInitialized = true;
132 | }
133 |
134 | // Do not add any additional code to this method
135 | private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
136 | {
137 | // Set the root visual to allow the application to render
138 | if (RootVisual != RootFrame)
139 | RootVisual = RootFrame;
140 |
141 | // Remove this handler since it is no longer needed
142 | RootFrame.Navigated -= CompleteInitializePhoneApplication;
143 | }
144 |
145 | #endregion
146 | }
147 | }
--------------------------------------------------------------------------------
/SudokuMaster/ApplicationIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/ApplicationIcon.png
--------------------------------------------------------------------------------
/SudokuMaster/Bin/Release/SudokuMaster.xap:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/Bin/Release/SudokuMaster.xap
--------------------------------------------------------------------------------
/SudokuMaster/BoardModel.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2014 Microsoft Mobile.
3 | */
4 |
5 |
6 | namespace SudokuMaster
7 | {
8 | public class BoardModel
9 | {
10 | public BoardValue[][] BoardNumbers { get; private set; }
11 |
12 | public BoardModel(int ColumnLength, int RowLength)
13 | {
14 | BoardNumbers = new BoardValue[RowLength][];
15 |
16 | for (int x = 0; x < BoardNumbers.Length; x++)
17 | BoardNumbers[x] = new BoardValue[ColumnLength];
18 |
19 | // create the actual objects
20 | for (int x = 0; x < BoardNumbers.Length; x++)
21 | {
22 | for (int y = 0; y < BoardNumbers[x].Length; y++)
23 | BoardNumbers[x][y] = new BoardValue();
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/SudokuMaster/BoardValue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.ComponentModel;
6 |
7 | namespace SudokuMaster
8 | {
9 | public class BoardValue : INotifyPropertyChanged
10 | {
11 | public event PropertyChangedEventHandler PropertyChanged;
12 |
13 | private int _value;
14 |
15 | ///
16 | /// Value representing number in particular cell
17 | ///
18 | public int Value
19 | {
20 | get
21 | {
22 | return _value;
23 | }
24 | set
25 | {
26 | _value = value;
27 | NotifyPropertyChanged("Value");
28 | }
29 | }
30 |
31 | private bool _setByGame;
32 |
33 | ///
34 | /// Flag indicating if the number was set before game started, or by user
35 | ///
36 | public bool SetByGame
37 | {
38 | get
39 | {
40 | return _setByGame;
41 | }
42 | set
43 | {
44 | _setByGame = value;
45 | NotifyPropertyChanged("SetByGame");
46 | }
47 | }
48 |
49 | private void NotifyPropertyChanged(string propertyName)
50 | {
51 | if (PropertyChanged != null)
52 | PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/SudokuMaster/Cell.xaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
17 |
18 |
19 |
20 |
21 |
24 |
25 |
26 |
27 |
30 |
31 |
32 |
33 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
47 |
48 |
49 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/SudokuMaster/Cell.xaml.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2014 Microsoft Mobile.
3 | */
4 |
5 | using System.Windows;
6 | using System.Windows.Controls;
7 | using System.Windows.Input;
8 | using System.Windows.Media;
9 |
10 | namespace SudokuMaster
11 | {
12 | ///
13 | /// Represents a single cell on the board grid
14 | ///
15 | public partial class Cell : UserControl
16 | {
17 | // dependency properties so that we can use databinding in our custom control
18 | public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(Cell), new PropertyMetadata(OnValueChanged));
19 | public static readonly DependencyProperty SetByGameProperty = DependencyProperty.Register("SetByGame", typeof(bool), typeof(Cell), new PropertyMetadata(true, OnSetByGameChanged));
20 |
21 | // static brushes - optimization so that we don't create them every single time
22 | private static SolidColorBrush whiteBrush = new SolidColorBrush(Colors.White);
23 | private static SolidColorBrush blackBrush = new SolidColorBrush(Colors.Black);
24 |
25 | #region properties
26 | public bool SetByGame
27 | {
28 | get { return (bool)GetValue(SetByGameProperty); }
29 | set { SetValue(SetByGameProperty, value); }
30 | }
31 |
32 | public int Value
33 | {
34 | get { return (int)GetValue(ValueProperty); }
35 | set { SetValue(ValueProperty, value); }
36 | }
37 |
38 | public bool IsPlayerSettable
39 | {
40 | get { return !SetByGame; }
41 | }
42 | #endregion
43 |
44 | ///
45 | /// Constructor
46 | ///
47 | /// Parent, or owner of this cell
48 | public Cell()
49 | {
50 | InitializeComponent();
51 | }
52 |
53 | ///
54 | /// Called when manipulation of the cell is completed.
55 | /// It's fired when user takes the finger off the screen,
56 | /// even if the fingers have drifted away from the control.
57 | ///
58 | /// Sender of the event
59 | /// Event arguments
60 | private void UserControl_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
61 | {
62 | // Start the fade out animation which returns the cell's color back to original.
63 | fadeOutAnimation.Begin();
64 |
65 | if (!IsPlayerSettable)
66 | SoundHelper.PlaySound(SoundHelper.SoundType.CellSelectedSound);
67 | else
68 | SoundHelper.PlaySound(SoundHelper.SoundType.NumberChosenSound);
69 | }
70 |
71 | ///
72 | /// Called when player touches the cell.
73 | /// Starts the fade in animation which gradually makes the cell red.
74 | ///
75 | /// Sender of the event.
76 | /// Event arguments.
77 | private void UserControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
78 | {
79 | fadeInAnimation.Begin();
80 | }
81 |
82 | ///
83 | /// Event fired when our SetByGame dependecy property change
84 | ///
85 | private static void OnSetByGameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
86 | {
87 | var cell = d as Cell;
88 | var value = (bool)e.NewValue;
89 |
90 | cell.textValue.Foreground = value ? blackBrush : whiteBrush;
91 | }
92 |
93 | ///
94 | /// Blink the cell
95 | ///
96 | public void Blink()
97 | {
98 | blinkAnimation.Begin();
99 | }
100 |
101 | ///
102 | /// Event fired when our Value dependecy property change
103 | ///
104 | private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
105 | {
106 | var cell = d as Cell;
107 | var value = (int)e.NewValue;
108 |
109 | if (value == 0)
110 | cell.textValue.Text = "";
111 | else
112 | cell.textValue.Text = value.ToString();
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/SudokuMaster/GameOver.xaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
15 |
16 |
17 |
49 |
50 |
51 |
52 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
65 |
66 |
69 |
73 |
77 |
78 |
79 |
82 |
83 |
86 |
87 |
88 |
89 |
90 |
91 |
96 |
97 |
98 |
99 |
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/SudokuMaster/GameOver.xaml.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2014 Microsoft Mobile.
3 | */
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Net;
9 | using System.Windows;
10 | using System.Windows.Controls;
11 | using System.Windows.Documents;
12 | using System.Windows.Input;
13 | using System.Windows.Media;
14 | using System.Windows.Media.Animation;
15 | using System.Windows.Shapes;
16 |
17 | namespace SudokuMaster
18 | {
19 | ///
20 | /// The dialog displayed when the puzzle is solved.
21 | /// Contains puzzle solving time and moves, and a texbox for player's name.
22 | ///
23 | public partial class GameOver : UserControl
24 | {
25 | HighscoreItem score;
26 |
27 | ///
28 | /// Constructor
29 | ///
30 | /// The score of the player. At least time and moves must be filled.
31 | public GameOver(HighscoreItem playerScore)
32 | {
33 | InitializeComponent();
34 |
35 | // Start the fade in animation
36 | fadeInAnimation.Begin();
37 |
38 | // Show the position textblock and player name textbox only if the
39 | // score is good enough to make it to the list.
40 | score = playerScore;
41 | int position = Highscores.IsNewHighscore(score);
42 | score.Index = position;
43 | if (position > 0)
44 | {
45 | playerName.Visibility = Visibility.Visible;
46 | textBlockPlacement.Text = "Your placement is " + position.ToString();
47 | }
48 | else
49 | {
50 | playerName.Visibility = Visibility.Collapsed;
51 | ConfirmButton.Content = "Ok";
52 | textBlockPlacement.Text = "";
53 | }
54 |
55 | textBlockTime.Text = "Your time was " + score.Time.ToString();
56 | }
57 |
58 | ///
59 | /// Called when the player presses a key on the keyboard
60 | ///
61 | /// Sender of the event.
62 | /// Event arguments.
63 | private void PlayerName_KeyDown(object sender, KeyEventArgs e)
64 | {
65 | // Add the score to the list and start fading out when the enter
66 | // key is pressed
67 | if (e.Key == Key.Enter)
68 | {
69 | Focus();
70 | }
71 | }
72 |
73 | ///
74 | /// Called when the fade out animation is completed
75 | ///
76 | /// Sender of the event
77 | /// Event arguments
78 | private void FadeOutAnimation_Completed(object sender, EventArgs e)
79 | {
80 | // Remove this control from the parent UI element (game grid). Fade
81 | // out is just a visual effect, and this control would still exist
82 | // and receive events when it's not visible.
83 | (Parent as Panel).Children.Remove(this);
84 | }
85 |
86 | private void Button_Click(object sender, RoutedEventArgs e)
87 | {
88 | if (score.Index > 0)
89 | {
90 | score.Name = playerName.Text;
91 | Highscores.AddNewHighscore(score);
92 | playerName.IsReadOnly = true;
93 | }
94 | fadeOutAnimation.Begin();
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/SudokuMaster/Gamelogic.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2014 Microsoft Mobile.
3 | */
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 |
10 | namespace SudokuMaster
11 | {
12 | ///
13 | /// Contains the logic for generating and managing puzzles
14 | ///
15 | public class GameLogic
16 | {
17 | public BoardModel Model { get; private set; }
18 |
19 | public const int BlockSize = 3;
20 | public const int BlocksPerSide = 3;
21 | public const int RowLength = BlockSize * BlocksPerSide;
22 | public const int ColumnLength = RowLength;
23 | public const int MaxEmptyCells = 45;
24 |
25 | protected int[] randOrder;
26 | protected bool solutionFound = false;
27 | private int[][] copyCells;
28 | protected Random randGen = new Random();
29 |
30 | public int EmptyCells { get; set; }
31 | public int PlayerMoves { get; set; }
32 |
33 | ///
34 | /// Constructor
35 | ///
36 | /// 9x9 array of Cells, the game board cells
37 | public GameLogic()
38 | {
39 | Model = new BoardModel(ColumnLength, RowLength);
40 | }
41 |
42 | ///
43 | /// Tests rows for conflicting cells
44 | ///
45 | /// X coordinate to check
46 | /// Y coordinate to check
47 | /// Number to test
48 | /// Tells whether we use the main array or a copy of it
49 | /// Conflicting point or null if no conflict
50 | private Point GetConflictsInRow(int x, int y, int value, bool useCopy)
51 | {
52 | for (int i = 0; i < RowLength; i++)
53 | {
54 | if (useCopy)
55 | {
56 | if (i != x &&
57 | copyCells[i][y] != 0 &&
58 | copyCells[i][y] == value)
59 | {
60 | return new Point(i, y);
61 | }
62 | }
63 | else
64 | {
65 | if (i != x &&
66 | Model.BoardNumbers[i][y].Value != 0 &&
67 | Model.BoardNumbers[i][y].Value == value)
68 | {
69 | return new Point(i, y);;
70 | }
71 | }
72 | }
73 |
74 | return null;
75 | }
76 |
77 | ///
78 | /// Tests columns for conflicting cells
79 | ///
80 | /// X coordinate to check
81 | /// Y coordinate to check
82 | /// Number to test
83 | /// Tells whether we use the main array or a copy of it
84 | /// Conflicting point or null if no conflict
85 | private Point GetConflictsInColumn(int x, int y, int value, bool useCopy)
86 | {
87 | for (int i = 0; i < ColumnLength; i++)
88 | {
89 | if (useCopy)
90 | {
91 | if (i != y &&
92 | copyCells[x][i] != 0 &&
93 | copyCells[x][i] == value)
94 | {
95 | return new Point(x, i);
96 | }
97 | }
98 | else
99 | {
100 | if (i != y &&
101 | Model.BoardNumbers[x][i].Value != 0 &&
102 | Model.BoardNumbers[x][i].Value == value)
103 | {
104 | return new Point(x, i);
105 | }
106 | }
107 | }
108 | return null;
109 | }
110 |
111 | ///
112 | /// Tests blocks (3x3) for conflicting cells
113 | ///
114 | /// X coordinate to check
115 | /// Y coordinate to check
116 | /// Number to test
117 | /// Tells whether we use the main array or a copy of it
118 | /// Conflicting point or null if no conflict
119 | private Point TestBlock(int x, int y, int value, bool useCopy)
120 | {
121 | int blocksFirstX = ((int)(x / 3)) * 3;
122 | int blocksFirstY = ((int)(y / 3)) * 3;
123 | for (int i = blocksFirstX; i < blocksFirstX + 3; i++)
124 | {
125 | for (int j = blocksFirstY; j < blocksFirstY + 3; j++)
126 | {
127 | if (useCopy)
128 | {
129 | if (copyCells[i][j] != 0)
130 | if (i != x && j != y && copyCells[i][j] == value)
131 | {
132 | return new Point(i, j);
133 | }
134 | }
135 | else
136 | {
137 | if (Model.BoardNumbers[i][j].Value != 0)
138 | if (i != x && j != y && Model.BoardNumbers[i][j].Value == value)
139 | {
140 | return new Point(i, j);
141 | }
142 | }
143 | }
144 | }
145 | return null;
146 | }
147 |
148 | ///
149 | /// Tries to set the given number to the given index.
150 | ///
151 | /// X coordinate on the grid
152 | /// Y coordinate on the grid
153 | /// Number to set
154 | /// Tells whether we use the main array or a copy of it
155 | /// true if it was possible, false otherwise.
156 | private bool SetNumber(int x, int y, int value, bool useCopy)
157 | {
158 | if (GetConflictsInRow(x, y, value, useCopy) == null &&
159 | GetConflictsInColumn(x, y, value, useCopy) == null &&
160 | TestBlock(x, y, value, useCopy) == null)
161 | {
162 | if (useCopy)
163 | copyCells[x][y] = value;
164 | else
165 | Model.BoardNumbers[x][y].Value = value;
166 |
167 | return true;
168 | }
169 | return false;
170 | }
171 |
172 | ///
173 | /// Copies cell values from main array to the copy array
174 | ///
175 | private void MakeCopy()
176 | {
177 | copyCells = new int[RowLength][];
178 |
179 | for (int i = 0; i < RowLength; i++)
180 | {
181 | copyCells[i] = new int[ColumnLength];
182 |
183 | for (int j = 0; j < ColumnLength; j++)
184 | copyCells[i][j] = Model.BoardNumbers[i][j].Value;
185 | }
186 | }
187 |
188 | ///
189 | /// Checks whether the puzzle has an unique solution.
190 | /// The algorithm is mainly the same as is fillCell().
191 | ///
192 | /// X coordinate on the grid
193 | /// Y coordinate on the grid
194 | /// true if if it does, false otherwise.
195 | public bool CheckUniqueness(int x, int y)
196 | {
197 | if (y == RowLength)
198 | {
199 | if (solutionFound)
200 | {
201 | solutionFound = false;
202 | return true;
203 | }
204 | solutionFound = true;
205 | return false;
206 | }
207 |
208 | int nextX = x + 1, nextY = y;
209 | if (x == RowLength - 1)
210 | {
211 | nextX = 0;
212 | nextY = y + 1;
213 | }
214 |
215 | if (Model.BoardNumbers[x][y].Value != 0)
216 | {
217 | if (CheckUniqueness(nextX, nextY))
218 | return true;
219 | }
220 | else
221 | {
222 | for (int i = 1; i <= 9; i++)
223 | if (SetNumber(x, y, i, true))
224 | {
225 | if (CheckUniqueness(nextX, nextY))
226 | return true;
227 |
228 | Model.BoardNumbers[x][y].Value = 0;
229 | }
230 | }
231 | return false;
232 | }
233 |
234 | ///
235 | /// Removes numbers from the board until a desired puzzle is obtained.
236 | /// Checks after every deletion that the puzzle still has an unique solution.
237 | ///
238 | private void RemoveCells()
239 | {
240 | while (EmptyCells < MaxEmptyCells)
241 | {
242 | int randX = randGen.Next(ColumnLength);
243 | int randY = randGen.Next(RowLength);
244 | int temp = Model.BoardNumbers[randX][randY].Value;
245 |
246 | if (temp != 0)
247 | {
248 | Model.BoardNumbers[randX][randY].Value = 0;
249 | MakeCopy();
250 |
251 | if (CheckUniqueness(0, 0))
252 | Model.BoardNumbers[randX][randY].Value = temp;
253 | else
254 | EmptyCells++;
255 | }
256 | }
257 | }
258 |
259 | ///
260 | /// Generates a random full sudoku board by using recursive
261 | /// backtracking method.
262 | ///
263 | /// X coordinate on the grid
264 | /// Y coordinate on the grid
265 | private bool FillCell(int x, int y)
266 | {
267 | if (y == RowLength)
268 | return true;
269 |
270 | int nextX = x + 1, nextY = y;
271 | if (x == ColumnLength - 1)
272 | {
273 | nextX = 0;
274 | nextY = y + 1;
275 | }
276 |
277 | for (int i = 0; i < RowLength; i++)
278 | {
279 | if (SetNumber(x, y, randOrder[i], false) && FillCell(nextX, nextY))
280 | return true;
281 |
282 | Model.BoardNumbers[x][y].Value = 0;
283 | }
284 | return false;
285 | }
286 |
287 | ///
288 | /// Creates an array with numbers 1-9 in it, ordered randomly.
289 | ///
290 | private void FillRandOrder()
291 | {
292 | randOrder = new int[RowLength];
293 | bool isSet = false;
294 | int rand, j;
295 | for (int i = 0; i < RowLength; i++)
296 | {
297 | while (!isSet)
298 | {
299 | rand = randGen.Next(ColumnLength) + 1;
300 | for (j = 0; j < ColumnLength; j++)
301 | if (rand == randOrder[j])
302 | break;
303 | if (j == ColumnLength)
304 | {
305 | randOrder[i] = rand;
306 | isSet = true;
307 | }
308 | }
309 | isSet = false;
310 | }
311 | }
312 |
313 | ///
314 | /// Generates a new puzzle
315 | ///
316 | public void GeneratePuzzle()
317 | {
318 | for (int i = 0; i < RowLength; i++)
319 | for (int j = 0; j < ColumnLength; j++)
320 | Model.BoardNumbers[i][j].Value = 0;
321 |
322 | FillRandOrder();
323 | FillCell(0, 0);
324 | EmptyCells = 0;
325 | PlayerMoves = 0;
326 | RemoveCells();
327 |
328 | for (int i = 0; i < RowLength; i++)
329 | {
330 | for (int j = 0; j < ColumnLength; j++)
331 | Model.BoardNumbers[i][j].SetByGame = Model.BoardNumbers[i][j].Value != 0;
332 | }
333 | }
334 |
335 | ///
336 | /// Places the number selected by the player on the game board
337 | ///
338 | /// X coordinate to set
339 | /// Y coordinate to set
340 | /// Number to set
341 | public List SetNumberByPlayer(int x, int y, int value)
342 | {
343 | var collisions = new List();
344 |
345 | var rowConflicts = GetConflictsInRow(x, y, value, false);
346 | var columnConflicts = GetConflictsInColumn(x, y, value, false);
347 | var blockConflicts = TestBlock(x, y, value, false);
348 |
349 | if (rowConflicts == null &&
350 | columnConflicts == null &&
351 | blockConflicts == null)
352 | {
353 | if (Model.BoardNumbers[x][y].Value != 0 && value == 0)
354 | EmptyCells++;
355 | else if (Model.BoardNumbers[x][y].Value == 0 && value != 0)
356 | EmptyCells--;
357 |
358 | Model.BoardNumbers[x][y].Value = value;
359 | }
360 |
361 | PlayerMoves++;
362 |
363 | if (rowConflicts != null)
364 | collisions.Add(rowConflicts);
365 |
366 | if (columnConflicts != null)
367 | collisions.Add(columnConflicts);
368 |
369 | if (blockConflicts != null)
370 | collisions.Add(blockConflicts);
371 |
372 | return collisions;
373 | }
374 | }
375 | }
376 |
--------------------------------------------------------------------------------
/SudokuMaster/HighscoreItem.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2014 Microsoft Mobile.
3 | */
4 |
5 | using System;
6 | using System.Xml;
7 | using System.Xml.Serialization;
8 |
9 | namespace SudokuMaster
10 | {
11 | ///
12 | /// Represents a single score in highscore list.
13 | ///
14 | public class HighscoreItem
15 | {
16 | public int Index { get; set; }
17 | public string Name { get; set; }
18 | public int Moves { get; set; }
19 |
20 | // XmlSerializer cannot serialize TimeSpans. Tell the serializer to
21 | // ignore Time, and (de)serialize the Time as string instead.
22 | [XmlIgnore]
23 | public TimeSpan Time { get; set; }
24 |
25 | [XmlAttribute("TimeString", DataType = "duration")]
26 | public string XmlTime
27 | {
28 | get { return XmlConvert.ToString(Time); }
29 | set { Time = XmlConvert.ToTimeSpan(value); }
30 | }
31 |
32 | ///
33 | /// Constructor
34 | /// Serializable classes must have default constructor without any
35 | /// arguments. XmlSerializer knows the member variables of given class,
36 | /// but nothing about the constructor's arguments.
37 | ///
38 | public HighscoreItem()
39 | {
40 | Index = 0;
41 | }
42 |
43 | ///
44 | /// Constructor
45 | ///
46 | /// Position in highscore list
47 | /// Player's name
48 | /// Time needed to solve the puzzle
49 | /// Moves needed to solve the puzzle
50 | public HighscoreItem(int scoreIndex, string playerName, TimeSpan solvingTime, int playerMoves)
51 | {
52 | Index = scoreIndex;
53 | Name = playerName;
54 | Time = solvingTime;
55 | Moves = playerMoves;
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/SudokuMaster/HighscoresPage.xaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
34 |
35 |
36 |
38 |
39 |
41 |
42 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/SudokuMaster/HighscoresPage.xaml.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2014 Microsoft Mobile.
3 | */
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.IO;
8 | using System.IO.IsolatedStorage;
9 | using System.Xml.Serialization;
10 | using Microsoft.Phone.Controls;
11 |
12 | namespace SudokuMaster
13 | {
14 | ///
15 | /// The highscores page.
16 | /// The page contains just the title and a listbox. Loading and saving
17 | /// the scores is done with XmlSerializer.
18 | ///
19 | public partial class Highscores : PhoneApplicationPage
20 | {
21 | const string highscoresFilename = "highscores.xml";
22 | public static List scores;
23 |
24 | ///
25 | /// Constructor
26 | /// Initializes the component and populates the listbox.
27 | ///
28 | public Highscores()
29 | {
30 | InitializeComponent();
31 | HighscoreList.ItemsSource = scores;
32 | }
33 |
34 | ///
35 | /// Loads the highscores from isolated storage
36 | ///
37 | static public void Load()
38 | {
39 | scores = new List();
40 | IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication();
41 |
42 | // Create empty list if the highscores file does not exist.
43 | // This is needed when the application is started for the first time.
44 | if (!store.FileExists(highscoresFilename))
45 | {
46 | for (int i = 1; i <= 20; i++)
47 | {
48 | scores.Add(new HighscoreItem(i, "Sudokumaster",
49 | new TimeSpan(0, 59, 59), 100));
50 | }
51 | Save();
52 | return;
53 | }
54 |
55 | // Open the file and use XmlSerializer to deserialize the xml file into
56 | // a list of HighscoreItems.
57 | using (IsolatedStorageFileStream stream = store.OpenFile(highscoresFilename, FileMode.Open))
58 | {
59 | using (StreamReader reader = new StreamReader(stream))
60 | {
61 | XmlSerializer serializer = new XmlSerializer(scores.GetType());
62 | scores = (List)serializer.Deserialize(reader);
63 | }
64 | }
65 | }
66 |
67 | ///
68 | /// Saves the highscores to isolated storage.
69 | ///
70 | static public void Save()
71 | {
72 | IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication();
73 |
74 | // Open the file and use XmlSerializer to serialize the list into the file
75 | using (IsolatedStorageFileStream stream = store.CreateFile(highscoresFilename))
76 | {
77 | using (StreamWriter writer = new StreamWriter(stream))
78 | {
79 | XmlSerializer serializer = new XmlSerializer(scores.GetType());
80 | serializer.Serialize(writer, scores);
81 | writer.Flush();
82 | }
83 | }
84 | }
85 |
86 | ///
87 | /// Checks if given score is a new highscore
88 | ///
89 | /// Score to check. The score should contain at least the solving time and moves.
90 | /// The position in highscore list, or zero if the score doesn't make it to the list
91 | static public int IsNewHighscore(HighscoreItem score)
92 | {
93 | foreach (HighscoreItem item in scores)
94 | {
95 | // Check the time, and if the times are the same, check the
96 | // moves needed to solve the puzzle
97 | if (score.Time < item.Time ||
98 | (score.Time == item.Time && score.Moves < item.Moves))
99 | return item.Index;
100 | }
101 |
102 | return 0;
103 | }
104 |
105 | ///
106 | /// Add a new score to highscore list
107 | ///
108 | /// Score to add. All members of the score should be filled.
109 | static public void AddNewHighscore(HighscoreItem score)
110 | {
111 | // Insert the score into the list, remove weakest score from the list
112 | // and save the list.
113 | if (score.Index <= 0)
114 | return;
115 | scores.Insert(score.Index - 1, score);
116 | scores.RemoveAt(scores.Count - 1);
117 | for (int t = score.Index; t < scores.Count; t++)
118 | scores[t].Index++;
119 | Save();
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/SudokuMaster/MainPage.xaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
23 |
24 |
26 |
27 |
28 |
29 |
30 |
31 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/SudokuMaster/MainPage.xaml.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2014 Microsoft Mobile.
3 | */
4 |
5 | using System;
6 | using System.IO;
7 | using System.IO.IsolatedStorage;
8 | using System.Linq;
9 | using System.Threading;
10 | using System.Windows;
11 | using System.Windows.Controls;
12 | using System.Windows.Data;
13 | using System.Windows.Input;
14 | using System.Windows.Media.Imaging;
15 | using System.Windows.Threading;
16 | using Microsoft.Phone.Controls;
17 | using Microsoft.Phone.Shell;
18 |
19 | namespace SudokuMaster
20 | {
21 | ///
22 | /// Main page of the application, the game itself
23 | ///
24 | public partial class MainPage : PhoneApplicationPage
25 | {
26 | ///
27 | /// Possible game states; game not started yet, ongoing and game over
28 | ///
29 | enum GameState
30 | {
31 | NotStarted = 0,
32 | Ongoing,
33 | GameOver
34 | };
35 |
36 | const String gameStateFile = "gamestate.dat";
37 | private GameLogic game;
38 | private GameState gameState = GameState.NotStarted;
39 | private DispatcherTimer gameTimer;
40 | private DateTime gameStartTime;
41 | private DateTime gamePausedTime;
42 | private TimeSpan gameTimeElapsed;
43 | private Cell[][] cells;
44 |
45 | ///
46 | /// Constructor
47 | ///
48 | public MainPage()
49 | {
50 | InitializeComponent();
51 |
52 | // Initialize the grid and instantiate the logic
53 | game = new GameLogic();
54 | gameTimer = new DispatcherTimer();
55 | gameTimer.Interval = TimeSpan.FromSeconds(1);
56 | gameTimer.Tick += StatusTimerTick;
57 |
58 | gamePausedTime = new DateTime();
59 |
60 | cells = CreateGrid();
61 |
62 | // For tombstoning; listen for deactivated event and restore state
63 | // if the application was deactivated earlier.
64 | PhoneApplicationService.Current.Deactivated += new EventHandler(App_Deactivated);
65 | RestoreState();
66 | }
67 |
68 | ///
69 | /// Called when a page becomes the active page in a frame.
70 | ///
71 | /// Event arguments
72 | protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
73 | {
74 | if (gameState == GameState.Ongoing && gamePausedTime > gameStartTime)
75 | {
76 | gameStartTime += DateTime.Now - gamePausedTime;
77 | UpdateStatus();
78 | }
79 | }
80 |
81 | ///
82 | /// Event handler for the highscores -button.
83 | /// Navigates to the highscores page.
84 | ///
85 | /// Sender of the event
86 | /// Event arguments
87 | private void HighscoresButton_Click(object sender, EventArgs e)
88 | {
89 | gamePausedTime = DateTime.Now;
90 |
91 | NavigationService.Navigate(new Uri("/HighscoresPage.xaml",
92 | UriKind.RelativeOrAbsolute));
93 | }
94 |
95 | ///
96 | /// Event handler for the new game -button
97 | ///
98 | /// Sender of the event.
99 | /// Event arguments.
100 | private void NewGameButton_Click(object sender, EventArgs e)
101 | {
102 | if (gameState == GameState.Ongoing)
103 | {
104 | MessageBoxResult result = MessageBox.Show("Do you really want to start new game? \nAny game progress will be lost.", "", MessageBoxButton.OKCancel);
105 |
106 | if (result == MessageBoxResult.OK)
107 | {
108 | NewGame();
109 | }
110 | }
111 | else
112 | NewGame();
113 | }
114 |
115 |
116 | ///
117 | /// Event handler for the status timer
118 | ///
119 | /// Sender of the event.
120 | /// Event arguments.
121 | private void StatusTimerTick(object sender, EventArgs e)
122 | {
123 | UpdateStatus();
124 | }
125 |
126 | ///
127 | /// Generates a new puzzle and starts the game
128 | ///
129 | private void NewGame()
130 | {
131 | // Close the GameOver dialog if it was still active
132 | GameOver gameOver = LayoutRoot.Children.OfType().SingleOrDefault();
133 | LayoutRoot.Children.Remove(gameOver);
134 |
135 | numberSelection.Visibility = System.Windows.Visibility.Collapsed;
136 |
137 | // Display wait note (spinning circle)
138 | waitIndicator.Visibility = System.Windows.Visibility.Visible;
139 | waitIndicator.StartSpin();
140 |
141 | // Disable databinding while generating puzzle
142 | DataContext = null;
143 |
144 | // Puzzle generation takes couple of seconds, do it in another thread
145 | ThreadPool.QueueUserWorkItem(dummy =>
146 | {
147 | // generating puzzle doesn't touch UI so it can run on another thread
148 | game.GeneratePuzzle();
149 |
150 | // switching to UI thread to modify UI components
151 | Deployment.Current.Dispatcher.BeginInvoke(() =>
152 | {
153 | DataContext = game.Model; // let's turn on databinding again
154 | gameTimer.Start();
155 | gameStartTime = DateTime.Now;
156 | gameState = GameState.Ongoing;
157 | UpdateStatus();
158 | waitIndicator.Visibility = System.Windows.Visibility.Collapsed;
159 | waitIndicator.StopSpin();
160 | });
161 | });
162 | }
163 |
164 | ///
165 | /// Updates status to UI; player moves, empty cells and game time
166 | ///
167 | private void UpdateStatus()
168 | {
169 | gameTimeElapsed = DateTime.Now - gameStartTime;
170 |
171 | GameTime.Text = String.Format("{0:D1}:{1:D2}:{2:D2}",
172 | gameTimeElapsed.Hours, gameTimeElapsed.Minutes, gameTimeElapsed.Seconds);
173 |
174 | Empty.Text = game.EmptyCells.ToString();
175 | Moves.Text = game.PlayerMoves.ToString();
176 |
177 | }
178 |
179 | ///
180 | /// Ends current game. Called when all the cells are filled.
181 | ///
182 | private void GameEnds()
183 | {
184 | gameTimer.Stop();
185 |
186 | // Blink all cells and prevent the player from modifying the cells
187 | for (int row = 0; row < GameLogic.RowLength; row++)
188 | {
189 | for (int col = 0; col < GameLogic.ColumnLength; col++)
190 | {
191 | game.Model.BoardNumbers[row][col].SetByGame = true; // to block the user input
192 | cells[row][col].Blink();
193 | }
194 | }
195 |
196 | // Display the score with GameOver dialog
197 | HighscoreItem score = new HighscoreItem();
198 | score.Time = new TimeSpan(gameTimeElapsed.Days, gameTimeElapsed.Hours,
199 | gameTimeElapsed.Minutes, gameTimeElapsed.Seconds, 0);
200 | score.Moves = game.PlayerMoves;
201 |
202 |
203 | //TODO: move this to XAML
204 | GameOver gameOver = new GameOver(score);
205 | // Main page is divided into 2x3 grid. Make sure the row and column
206 | // properties are set properly (position 0,0 with span 2,3) to make
207 | // the dialog visible anywhere on the page.
208 | gameOver.SetValue(Grid.RowSpanProperty, 3);
209 | gameOver.SetValue(Grid.ColumnSpanProperty, 2);
210 | gameOver.SetValue(Grid.VerticalAlignmentProperty, VerticalAlignment.Center);
211 | gameOver.SetValue(Grid.HorizontalAlignmentProperty, HorizontalAlignment.Center);
212 | gameOver.SetValue(MarginProperty, new Thickness(10, 0, 0, 0));
213 |
214 | LayoutRoot.Children.Add(gameOver);
215 | gameState = GameState.GameOver;
216 |
217 | SoundHelper.PlaySound(SoundHelper.SoundType.GameEndSound);
218 | }
219 |
220 | ///
221 | /// Creates the grid cells and populates the board grid with the cells
222 | ///
223 | /// 9x9 array of empty cells
224 | private Cell[][] CreateGrid()
225 | {
226 | var darkImage = new BitmapImage(new Uri("/gfx/darkGridItem.png", UriKind.Relative));
227 | var lightImage = new BitmapImage(new Uri("/gfx/lightGridItem.png", UriKind.Relative));
228 |
229 | bool lightCell = false;
230 | Cell[][] cells = new Cell[GameLogic.RowLength][];
231 |
232 | for (int row = 0; row < GameLogic.RowLength; row++)
233 | {
234 | cells[row] = new Cell[GameLogic.ColumnLength];
235 |
236 | if (row % GameLogic.BlockSize != 0)
237 | lightCell = !lightCell;
238 |
239 | for (int col = 0; col < GameLogic.ColumnLength; col++)
240 | {
241 | // switch image type (light or dark) after each 3 cells in row
242 | if (col % GameLogic.BlockSize == 0)
243 | lightCell = !lightCell;
244 |
245 | Cell c = new Cell();
246 | c.SetValue(Grid.RowProperty, row);
247 | c.SetValue(Grid.ColumnProperty, col);
248 | c.BackgroundImage.Source = lightCell ? lightImage : darkImage;
249 |
250 | // install event handler
251 | c.MouseLeftButtonDown += new MouseButtonEventHandler(OnCellTouched);
252 |
253 | // set binding to proper BoardValue from BoardViewModel
254 | Binding b = new Binding(string.Format("BoardNumbers[{0}][{1}].Value", row, col));
255 | c.SetBinding(Cell.ValueProperty, b);
256 |
257 | Binding b2 = new Binding(string.Format("BoardNumbers[{0}][{1}].SetByGame", row, col));
258 | c.SetBinding(Cell.SetByGameProperty, b2);
259 |
260 | cells[row][col] = c;
261 | BoardGrid.Children.Add(c);
262 | }
263 | }
264 |
265 | return cells;
266 | }
267 |
268 | private void OnCellTouched(object sender, MouseButtonEventArgs e)
269 | {
270 | Cell cell = sender as Cell;
271 |
272 | if (!cell.IsPlayerSettable)
273 | return;
274 |
275 | // This lambda experssion will allow us to have access to destination cell in a clean way
276 | numberSelection.OnSelectedNumber = (selectedNumber => OnNumberChoosen(cell, selectedNumber));
277 |
278 | // Place the dialog above the cell, but make sure the dialog fits on the screen.
279 | numberSelection.KeyboardMargin = GetPositionForCell(cell);
280 |
281 | // Change the visibility + start fade in animation
282 | numberSelection.ShowKeyboard();
283 | }
284 |
285 | ///
286 | /// Helper method to get the absolute position with respect to the screen borders
287 | ///
288 | private Thickness GetPositionForCell(Cell cell)
289 | {
290 | var pos = new System.Windows.Point(cell.ActualWidth / 2 - numberSelection.KeyboardSize.Width / 2, cell.ActualHeight / 2 - numberSelection.KeyboardSize.Height / 2);
291 | pos = cell.TransformToVisual(LayoutRoot).Transform(pos);
292 |
293 | if (pos.X < 0)
294 | pos.X = 0;
295 | else if (pos.X > LayoutRoot.ActualWidth - numberSelection.KeyboardSize.Width)
296 | pos.X = LayoutRoot.ActualWidth - numberSelection.KeyboardSize.Width;
297 |
298 | if (pos.Y < 0)
299 | pos.Y = 0;
300 | else if (pos.Y > LayoutRoot.ActualHeight - numberSelection.KeyboardSize.Height)
301 | pos.Y = LayoutRoot.ActualHeight - numberSelection.KeyboardSize.Height;
302 |
303 | return new Thickness(pos.X, pos.Y, 0, 0);
304 | }
305 |
306 | ///
307 | /// Action triggered when user selected a number
308 | ///
309 | private void OnNumberChoosen(Cell sender, int number)
310 | {
311 | var conflictingCells = game.SetNumberByPlayer((int)sender.GetValue(Grid.RowProperty),
312 | (int)sender.GetValue(Grid.ColumnProperty),
313 | number);
314 |
315 | foreach (var point in conflictingCells)
316 | cells[point.X][point.Y].Blink();
317 |
318 | SoundHelper.PlaySound(SoundHelper.SoundType.CellSelectedSound);
319 |
320 | if (gameState != GameState.NotStarted && game.EmptyCells == 0)
321 | GameEnds();
322 | }
323 |
324 | ///
325 | /// Event handler for application deactivation.
326 | /// Stores the current game state into a file.
327 | ///
328 | /// Sender of the event.
329 | /// Event arguments.
330 | void App_Deactivated(object sender, DeactivatedEventArgs e)
331 | {
332 | StoreState();
333 | }
334 |
335 | ///
336 | /// Reads the game state from a file and continues the game from where
337 | /// it was left.
338 | ///
339 | private void RestoreState()
340 | {
341 | IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication();
342 | if (!store.FileExists(gameStateFile))
343 | return;
344 |
345 | int emptyCells = 0;
346 | using (IsolatedStorageFileStream stream = store.OpenFile(gameStateFile, FileMode.Open))
347 | {
348 | using (BinaryReader reader = new BinaryReader(stream))
349 | {
350 | // Read the state and stats
351 | gameState = (GameState)reader.ReadInt32();
352 | game.PlayerMoves = reader.ReadInt32();
353 | gameTimeElapsed = new TimeSpan(reader.ReadInt64());
354 | gameStartTime = DateTime.Now - gameTimeElapsed;
355 |
356 | // Read contents of the cells
357 | for (int row = 0; row < GameLogic.RowLength; row++)
358 | {
359 | for (int col = 0; col < GameLogic.ColumnLength; col++)
360 | {
361 | int value = reader.ReadInt32();
362 | game.Model.BoardNumbers[row][col].Value = value;
363 | game.Model.BoardNumbers[row][col].SetByGame = reader.ReadBoolean();
364 |
365 | if (value == 0)
366 | emptyCells++;
367 | }
368 | }
369 | }
370 | }
371 |
372 | store.DeleteFile(gameStateFile);
373 |
374 | if (gameState == GameState.Ongoing)
375 | {
376 | game.EmptyCells = emptyCells;
377 | gameTimer.Start();
378 | }
379 | else
380 | {
381 | game.EmptyCells = 0;
382 | }
383 |
384 | DataContext = game.Model;
385 | UpdateStatus();
386 | }
387 |
388 | ///
389 | /// Stores current game state to a file
390 | ///
391 | private void StoreState()
392 | {
393 | IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication();
394 |
395 | using (IsolatedStorageFileStream stream = store.CreateFile(gameStateFile))
396 | {
397 | using (BinaryWriter writer = new BinaryWriter(stream))
398 | {
399 |
400 | writer.Write((Int32)gameState);
401 | writer.Write(game.PlayerMoves);
402 | writer.Write((Int64)gameTimeElapsed.Ticks);
403 |
404 | // Contents of the cells
405 | for (int row = 0; row < GameLogic.RowLength; row++)
406 | {
407 | for (int col = 0; col < GameLogic.ColumnLength; col++)
408 | {
409 | writer.Write(game.Model.BoardNumbers[row][col].Value);
410 | writer.Write(game.Model.BoardNumbers[row][col].SetByGame);
411 | }
412 | }
413 | }
414 | }
415 | }
416 |
417 | ///
418 | /// Event handler for orientation changes.
419 | /// Repositions UI elements depending on the orientation.
420 | ///
421 | /// Sender of the event
422 | /// Event arguments
423 | private void PhoneApplicationPage_OrientationChanged(object sender,OrientationChangedEventArgs e)
424 | {
425 | if (e.Orientation == PageOrientation.Landscape ||
426 | e.Orientation == PageOrientation.LandscapeLeft ||
427 | e.Orientation == PageOrientation.LandscapeRight)
428 | {
429 | Logo.SetValue(Grid.RowProperty, 1);
430 | Logo.SetValue(Grid.ColumnSpanProperty, 1);
431 |
432 | BoardGrid.SetValue(Grid.RowProperty, 0);
433 | BoardGrid.SetValue(Grid.ColumnProperty, 1);
434 | BoardGrid.SetValue(Grid.RowSpanProperty, 3);
435 | BoardGrid.SetValue(Grid.ColumnSpanProperty, 2);
436 |
437 | waitIndicator.SetValue(Grid.RowProperty, 0);
438 | waitIndicator.SetValue(Grid.ColumnProperty, 1);
439 | waitIndicator.SetValue(Grid.RowSpanProperty, 3);
440 | waitIndicator.SetValue(Grid.ColumnSpanProperty, 2);
441 |
442 | Statistics.SetValue(Grid.RowProperty, 1);
443 | Statistics.SetValue(Grid.RowSpanProperty, 2);
444 | Statistics.SetValue(Grid.ColumnSpanProperty, 1);
445 |
446 | if(e.Orientation == PageOrientation.LandscapeLeft)
447 | LayoutRoot.Margin = new Thickness(0 ,0 ,72 ,0);
448 | if (e.Orientation == PageOrientation.LandscapeRight)
449 | LayoutRoot.Margin = new Thickness(72, 0, 0, 0);
450 |
451 | LayoutRoot.RowDefinitions[0].Height = new GridLength(90);
452 | LayoutRoot.RowDefinitions[1].Height = new GridLength(90);
453 |
454 | for (int t = 0; t < Statistics.ColumnDefinitions.Count; t++)
455 | Statistics.ColumnDefinitions[t].Width = new GridLength(0);
456 |
457 | Statistics.ColumnDefinitions[0].Width = new GridLength(10);
458 | Statistics.ColumnDefinitions[1].Width = new GridLength(35, GridUnitType.Star);
459 | Statistics.ColumnDefinitions[2].Width = new GridLength(65, GridUnitType.Star);
460 |
461 | Statistics.RowDefinitions[0].Height = new GridLength(10);
462 | Statistics.RowDefinitions[1].Height = new GridLength(100, GridUnitType.Star);
463 | Statistics.RowDefinitions[2].Height = new GridLength(100, GridUnitType.Star);
464 | Statistics.RowDefinitions[3].Height = new GridLength(100, GridUnitType.Star);
465 | Statistics.RowDefinitions[4].Height = new GridLength(10);
466 |
467 | Statistics.Height = 192;
468 |
469 | MovesImage.SetValue(Grid.ColumnProperty, 1);
470 | MovesImage.SetValue(Grid.RowProperty, 1);
471 |
472 | EmptyImage.SetValue(Grid.ColumnProperty, 1);
473 | EmptyImage.SetValue(Grid.RowProperty, 2);
474 |
475 | GameTimeImage.SetValue(Grid.ColumnProperty, 1);
476 | GameTimeImage.SetValue(Grid.RowProperty, 3);
477 |
478 | Moves.SetValue(Grid.ColumnProperty, 2);
479 | Moves.SetValue(Grid.RowProperty, 1);
480 |
481 | Empty.SetValue(Grid.ColumnProperty, 2);
482 | Empty.SetValue(Grid.RowProperty, 2);
483 |
484 | GameTime.SetValue(Grid.ColumnProperty, 2);
485 | GameTime.SetValue(Grid.RowProperty, 3);
486 | }
487 | else
488 | {
489 | Logo.SetValue(Grid.RowProperty, 0);
490 | Logo.SetValue(Grid.ColumnSpanProperty, 2);
491 |
492 | BoardGrid.SetValue(Grid.RowProperty, 1);
493 | BoardGrid.SetValue(Grid.ColumnProperty, 0);
494 | BoardGrid.SetValue(Grid.RowSpanProperty, 1);
495 | BoardGrid.SetValue(Grid.ColumnSpanProperty, 2);
496 |
497 | waitIndicator.SetValue(Grid.RowProperty, 1);
498 | waitIndicator.SetValue(Grid.ColumnProperty, 0);
499 | waitIndicator.SetValue(Grid.RowSpanProperty, 1);
500 | waitIndicator.SetValue(Grid.ColumnSpanProperty, 2);
501 |
502 | Statistics.SetValue(Grid.RowProperty, 3);
503 | Statistics.SetValue(Grid.RowSpanProperty, 1);
504 | Statistics.SetValue(Grid.ColumnSpanProperty, 2);
505 |
506 | LayoutRoot.Margin = new Thickness(0, 0, 0, 72);
507 | LayoutRoot.RowDefinitions[0].Height = new GridLength(120);
508 | LayoutRoot.RowDefinitions[1].Height = new GridLength(460);
509 |
510 | for (int t = 0; t < Statistics.RowDefinitions.Count; t++)
511 | Statistics.RowDefinitions[t].Height = new GridLength(0);
512 |
513 | Statistics.ColumnDefinitions[0].Width = new GridLength(18);
514 | Statistics.ColumnDefinitions[1].Width = new GridLength(60, GridUnitType.Star);
515 | Statistics.ColumnDefinitions[2].Width = new GridLength(75, GridUnitType.Star);
516 | Statistics.ColumnDefinitions[3].Width = new GridLength(60, GridUnitType.Star);
517 | Statistics.ColumnDefinitions[4].Width = new GridLength(75, GridUnitType.Star);
518 | Statistics.ColumnDefinitions[5].Width = new GridLength(60, GridUnitType.Star);
519 | Statistics.ColumnDefinitions[6].Width = new GridLength(90, GridUnitType.Star);
520 | Statistics.ColumnDefinitions[7].Width = new GridLength(18);
521 |
522 | Statistics.RowDefinitions[0].Height = new GridLength(100, GridUnitType.Star);
523 |
524 | Statistics.Height = 64;
525 |
526 | MovesImage.SetValue(Grid.ColumnProperty, 1);
527 | MovesImage.SetValue(Grid.RowProperty, 0);
528 |
529 | EmptyImage.SetValue(Grid.ColumnProperty, 3);
530 | EmptyImage.SetValue(Grid.RowProperty, 0);
531 |
532 | GameTimeImage.SetValue(Grid.ColumnProperty, 5);
533 | GameTimeImage.SetValue(Grid.RowProperty, 0);
534 |
535 | Moves.SetValue(Grid.ColumnProperty, 2);
536 | Moves.SetValue(Grid.RowProperty, 0);
537 |
538 | Empty.SetValue(Grid.ColumnProperty, 4);
539 | Empty.SetValue(Grid.RowProperty, 0);
540 |
541 | GameTime.SetValue(Grid.ColumnProperty, 6);
542 | GameTime.SetValue(Grid.RowProperty, 0);
543 | }
544 | }
545 | }
546 | }
547 |
--------------------------------------------------------------------------------
/SudokuMaster/MemoryDiagnosticsControl.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2014 Microsoft Mobile.
3 | */
4 |
5 | using System;
6 | using System.Windows;
7 | using System.Windows.Controls;
8 | using System.Windows.Controls.Primitives;
9 | using System.Windows.Media;
10 | using System.Windows.Threading;
11 | using Microsoft.Phone.Info;
12 |
13 | namespace SudokuMaster.Debug
14 | {
15 | ///
16 | /// Helper class for showing current memory usage.
17 | /// Run only in debug mode.
18 | ///
19 |
20 | public static class MemoryDiagnosticsControl
21 | {
22 | static Popup _popup;
23 | static TextBlock _currentMemoryBlock;
24 | static DispatcherTimer _timer;
25 | static bool _forceGc;
26 |
27 | ///
28 | /// Show the memory counter
29 | ///
30 | /// Whether or not to do automatic garbage collection each tick
31 |
32 | public static void Start(bool forceGc)
33 | {
34 | _forceGc = forceGc;
35 |
36 | CreatePopup();
37 | CreateTimer();
38 | ShowPopup();
39 | StartTimer();
40 | }
41 |
42 | ///
43 | /// Stop the memory counter
44 | ///
45 |
46 | public static void Stop()
47 | {
48 | HidePopup();
49 | StopTimer();
50 | }
51 |
52 | ///
53 | /// Show the popup
54 | ///
55 |
56 | static void ShowPopup()
57 | {
58 | _popup.IsOpen = true;
59 | }
60 |
61 | static void StartTimer()
62 | {
63 | _timer.Start();
64 | }
65 |
66 | static void CreateTimer()
67 | {
68 | if (_timer != null)
69 | return;
70 |
71 | _timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(300) };
72 | _timer.Tick += TimerTick;
73 | }
74 |
75 |
76 | static void TimerTick(object sender, EventArgs e)
77 | {
78 | // call Garbage collector before getting memory usage
79 | if (_forceGc)
80 | GC.Collect();
81 | var mem = (long)DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage");
82 | _currentMemoryBlock.Text = string.Format("{0:N}", mem / 1024);
83 | }
84 |
85 | static void CreatePopup()
86 | {
87 |
88 | if (_popup != null)
89 |
90 | return;
91 | _popup = new Popup();
92 | var fontSize = (double)Application.Current.Resources["PhoneFontSizeSmall"] - 2;
93 | var foreground = (Brush)Application.Current.Resources["PhoneForegroundBrush"];
94 | var sp = new StackPanel { Orientation = Orientation.Horizontal, Background = (Brush)Application.Current.Resources["PhoneSemitransparentBrush"] };
95 | _currentMemoryBlock = new TextBlock { Text = "---", FontSize = fontSize, Foreground = foreground };
96 | sp.Children.Add(new TextBlock { Text = "Mem(kB): ", FontSize = fontSize, Foreground = foreground });
97 | sp.Children.Add(_currentMemoryBlock);
98 | sp.RenderTransform = new CompositeTransform { Rotation = 90, TranslateX = 480, TranslateY = 420, CenterX = 0, CenterY = 0 };
99 | _popup.Child = sp;
100 | }
101 |
102 | static void StopTimer()
103 | {
104 | _timer.Stop();
105 | }
106 |
107 | static void HidePopup()
108 | {
109 | _popup.IsOpen = false;
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/SudokuMaster/NumberSelection.xaml:
--------------------------------------------------------------------------------
1 |
4 |
14 |
15 |
16 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
94 |
95 |
96 |
97 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/SudokuMaster/NumberSelection.xaml.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2014 Microsoft Mobile.
3 | */
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Net;
9 | using System.Windows;
10 | using System.Windows.Controls;
11 | using System.Windows.Documents;
12 | using System.Windows.Input;
13 | using System.Windows.Media;
14 | using System.Windows.Media.Animation;
15 | using System.Windows.Shapes;
16 |
17 | namespace SudokuMaster
18 | {
19 | ///
20 | /// Number selection control which is shown when the player touches a cell on the game grid
21 | ///
22 | public partial class NumberSelection : UserControl
23 | {
24 | private SolidColorBrush blackBrush = new SolidColorBrush(Colors.Black);
25 | private SolidColorBrush whiteBrush = new SolidColorBrush(Colors.White);
26 |
27 | #region properties
28 | public Thickness KeyboardMargin
29 | {
30 | get { return keyboardGrid.Margin; }
31 | set { keyboardGrid.Margin = value; }
32 | }
33 |
34 | public Size KeyboardSize
35 | {
36 | get { return new Size(keyboardGrid.Width, keyboardGrid.Height); }
37 | }
38 |
39 | public Action OnClickOutside { get; set; }
40 | public Action OnSelectedNumber { get; set; }
41 | #endregion
42 |
43 | ///
44 | /// Constructor
45 | ///
46 | /// The cell select by the player for manipulation
47 | public NumberSelection()
48 | {
49 | InitializeComponent();
50 | }
51 |
52 | ///
53 | /// Called when the player touches the screen outside of this control
54 | ///
55 | /// Sender of the event
56 | /// Event arguments
57 | public void OnClickBackground(object sender, MouseButtonEventArgs e)
58 | {
59 | // Do not receive these events anymore and start fading out this control
60 | fadeOutAnimation.Begin();
61 |
62 | if (OnClickOutside != null)
63 | OnClickOutside();
64 | }
65 |
66 | public void ShowKeyboard()
67 | {
68 | fadeInAnimation.Begin();
69 | Visibility = System.Windows.Visibility.Visible;
70 | }
71 |
72 | ///
73 | /// Called when the fade out animation is completed
74 | ///
75 | /// Sender of the event
76 | /// Event arguments
77 | private void FadeOutAnimation_Completed(object sender, EventArgs e)
78 | {
79 | this.Visibility = System.Windows.Visibility.Collapsed;
80 | }
81 |
82 | ///
83 | /// Called when the player touches any one of the buttons
84 | ///
85 | /// Sender of the event
86 | /// Event arguments
87 | private void Button_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
88 | {
89 | // Invert the colors of touched text block and its backgroud
90 | (sender as Button).Background = whiteBrush;
91 | }
92 |
93 | ///
94 | /// Called when the player is not touching the button anymore
95 | ///
96 | /// Sender of the event
97 | /// Event arguments
98 | private void Button_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
99 | {
100 | // Invert the colors of touched text block and its backgroud
101 | fadeOutAnimation.Begin();
102 | SoundHelper.PlaySound(SoundHelper.SoundType.NumberChosenSound);
103 |
104 | // Get the value of the text block player pressed and pass it to
105 | // the game logic. The cell will be cleared if the value is zero.
106 | int val = 0;
107 | Int32.TryParse((sender as Button).Content.ToString(), out val);
108 |
109 | if (OnSelectedNumber != null)
110 | OnSelectedNumber(val);
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/SudokuMaster/Point.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2014 Microsoft Mobile.
3 | */
4 |
5 | using System;
6 | using System.Net;
7 | using System.Windows;
8 | using System.Windows.Controls;
9 | using System.Windows.Documents;
10 | using System.Windows.Ink;
11 | using System.Windows.Input;
12 | using System.Windows.Media;
13 | using System.Windows.Media.Animation;
14 | using System.Windows.Shapes;
15 |
16 | namespace SudokuMaster
17 | {
18 | public class Point
19 | {
20 | public int X;
21 | public int Y;
22 |
23 | public Point(int x, int y)
24 | {
25 | this.X = x;
26 | this.Y = y;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/SudokuMaster/Properties/AppManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/SudokuMaster/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("SudokuMaster")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Nokia Corporation")]
12 | [assembly: AssemblyProduct("SudokuMaster")]
13 | [assembly: AssemblyCopyright("Copyright © 2011-2014 Microsoft Mobile")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("95ca3a42-5a5f-4ff1-acfd-4c934d3e0079")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Revision and Build Numbers
33 | // by using the '*' as shown below:
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
36 |
--------------------------------------------------------------------------------
/SudokuMaster/Properties/WMAppManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ApplicationIcon.png
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | Background.png
26 | 0
27 | SudokuMaster
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/SudokuMaster/SoundHelper.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2014 Microsoft Mobile.
3 | */
4 |
5 | using Microsoft.Xna.Framework;
6 | using Microsoft.Xna.Framework.Audio;
7 |
8 | namespace SudokuMaster
9 | {
10 | public class SoundHelper
11 | {
12 | public enum SoundType
13 | {
14 | GameEndSound,
15 | CellSelectedSound,
16 | NumberChosenSound
17 | }
18 |
19 | private static SoundEffect gameEndEffect = LoadSound("sounds/60443__jobro__tada1.wav");
20 | private static SoundEffect cellSelectedEffect = LoadSound("sounds/7040__yawfle__050816_chair_04.wav");
21 | private static SoundEffect numberChosenEffect = LoadSound("sounds/7043__yawfle__050816_chair_07.wav");
22 |
23 | private static SoundEffect LoadSound(string path)
24 | {
25 | using (var stream = TitleContainer.OpenStream(path))
26 | {
27 | return SoundEffect.FromStream(stream);
28 | }
29 | }
30 |
31 | ///
32 | /// Playes given sound file
33 | ///
34 | /// Path and name of a wav file to be played
35 | static public void PlaySound(SoundType type)
36 | {
37 | //using (var stream = TitleContainer.OpenStream(soundFile))
38 | //{
39 | // var effect = SoundEffect.FromStream(stream);
40 | // FrameworkDispatcher.Update();
41 | // effect.Play();
42 | //}
43 | FrameworkDispatcher.Update();
44 | switch (type)
45 | {
46 | case SoundType.CellSelectedSound:
47 | cellSelectedEffect.Play();
48 | break;
49 | case SoundType.GameEndSound:
50 | gameEndEffect.Play();
51 | break;
52 | case SoundType.NumberChosenSound:
53 | numberChosenEffect.Play();
54 | break;
55 |
56 | }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/SudokuMaster/SplashScreenImage.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/SplashScreenImage.jpg
--------------------------------------------------------------------------------
/SudokuMaster/SudokuMaster.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 10.0.20506
7 | 2.0
8 | {8062FAC1-CF4C-4056-B3B7-5DFFE9EEF4B5}
9 | {C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}
10 | Library
11 | Properties
12 | SudokuMaster
13 | SudokuMaster
14 | v4.0
15 | $(TargetFrameworkVersion)
16 | WindowsPhone
17 | Silverlight
18 | true
19 |
20 |
21 | true
22 | true
23 | SudokuMaster.xap
24 | Properties\AppManifest.xml
25 | SudokuMaster.App
26 | true
27 | true
28 |
29 |
30 | true
31 | full
32 | false
33 | Bin\Debug
34 | DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE
35 | true
36 | true
37 | prompt
38 | 4
39 |
40 |
41 | pdbonly
42 | true
43 | Bin\Release
44 | TRACE;SILVERLIGHT;WINDOWS_PHONE
45 | true
46 | true
47 | prompt
48 | 4
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | App.xaml
64 |
65 |
66 |
67 |
68 | Cell.xaml
69 |
70 |
71 |
72 | GameOver.xaml
73 |
74 |
75 |
76 | HighscoresPage.xaml
77 |
78 |
79 | MainPage.xaml
80 |
81 |
82 |
83 | NumberSelection.xaml
84 |
85 |
86 |
87 |
88 |
89 | WaitNote.xaml
90 |
91 |
92 |
93 |
94 | Designer
95 | MSBuild:Compile
96 |
97 |
98 | Designer
99 | MSBuild:Compile
100 |
101 |
102 | Designer
103 | MSBuild:Compile
104 |
105 |
106 | Designer
107 | MSBuild:Compile
108 |
109 |
110 | Designer
111 | MSBuild:Compile
112 |
113 |
114 | Designer
115 | MSBuild:Compile
116 |
117 |
118 | Designer
119 | MSBuild:Compile
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | Always
129 |
130 |
131 | PreserveNewest
132 |
133 |
134 | PreserveNewest
135 |
136 |
137 | PreserveNewest
138 |
139 |
140 | PreserveNewest
141 |
142 |
143 | PreserveNewest
144 |
145 |
146 | PreserveNewest
147 |
148 |
149 | PreserveNewest
150 |
151 |
152 | PreserveNewest
153 |
154 |
155 | PreserveNewest
156 |
157 |
158 | PreserveNewest
159 |
160 |
161 | PreserveNewest
162 |
163 |
164 | Always
165 |
166 |
167 | Always
168 |
169 |
170 | Always
171 |
172 |
173 |
174 | PreserveNewest
175 |
176 |
177 | PreserveNewest
178 |
179 |
180 |
181 |
182 |
183 |
190 |
191 |
--------------------------------------------------------------------------------
/SudokuMaster/WaitNote.xaml:
--------------------------------------------------------------------------------
1 |
4 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/SudokuMaster/WaitNote.xaml.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2014 Microsoft Mobile.
3 | */
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Net;
9 | using System.Windows;
10 | using System.Windows.Controls;
11 | using System.Windows.Documents;
12 | using System.Windows.Input;
13 | using System.Windows.Media;
14 | using System.Windows.Media.Animation;
15 | using System.Windows.Shapes;
16 | using System.Globalization;
17 |
18 | namespace SudokuMaster
19 | {
20 | ///
21 | /// Wait note, spinning circle animation
22 | ///
23 | public partial class WaitNote : UserControl
24 | {
25 | ///
26 | /// Constructor
27 | /// Starts the spin animation immediately.
28 | ///
29 | public WaitNote()
30 | {
31 | InitializeComponent();
32 | }
33 |
34 |
35 | public void StartSpin()
36 | {
37 | spinAnimation.Begin();
38 | }
39 |
40 | public void StopSpin()
41 | {
42 | spinAnimation.Stop();
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/SudokuMaster/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/background.png
--------------------------------------------------------------------------------
/SudokuMaster/gfx/back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/gfx/back.png
--------------------------------------------------------------------------------
/SudokuMaster/gfx/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/gfx/background.png
--------------------------------------------------------------------------------
/SudokuMaster/gfx/brownGridItem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/gfx/brownGridItem.png
--------------------------------------------------------------------------------
/SudokuMaster/gfx/darkGridItem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/gfx/darkGridItem.png
--------------------------------------------------------------------------------
/SudokuMaster/gfx/empty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/gfx/empty.png
--------------------------------------------------------------------------------
/SudokuMaster/gfx/highscores.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/gfx/highscores.png
--------------------------------------------------------------------------------
/SudokuMaster/gfx/lightGridItem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/gfx/lightGridItem.png
--------------------------------------------------------------------------------
/SudokuMaster/gfx/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/gfx/logo.png
--------------------------------------------------------------------------------
/SudokuMaster/gfx/move.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/gfx/move.png
--------------------------------------------------------------------------------
/SudokuMaster/gfx/newgame.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/gfx/newgame.png
--------------------------------------------------------------------------------
/SudokuMaster/gfx/statistic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/gfx/statistic.png
--------------------------------------------------------------------------------
/SudokuMaster/gfx/time.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/gfx/time.png
--------------------------------------------------------------------------------
/SudokuMaster/gfx/waitNote.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/gfx/waitNote.png
--------------------------------------------------------------------------------
/SudokuMaster/sounds/60443__jobro__tada1.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/sounds/60443__jobro__tada1.wav
--------------------------------------------------------------------------------
/SudokuMaster/sounds/7040__yawfle__050816_chair_04.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/sounds/7040__yawfle__050816_chair_04.wav
--------------------------------------------------------------------------------
/SudokuMaster/sounds/7043__yawfle__050816_chair_07.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/SudokuMaster/sounds/7043__yawfle__050816_chair_07.wav
--------------------------------------------------------------------------------
/doc/ClassDiagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/doc/ClassDiagram.png
--------------------------------------------------------------------------------
/doc/ScreenShot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/doc/ScreenShot.png
--------------------------------------------------------------------------------
/doc/porting/Qt_Highscores.2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/doc/porting/Qt_Highscores.2.png
--------------------------------------------------------------------------------
/doc/porting/Qt_Highscores.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/doc/porting/Qt_Highscores.png
--------------------------------------------------------------------------------
/doc/porting/Qt_Landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/doc/porting/Qt_Landscape.png
--------------------------------------------------------------------------------
/doc/porting/Qt_MainView.2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/doc/porting/Qt_MainView.2.png
--------------------------------------------------------------------------------
/doc/porting/Qt_MainView.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/doc/porting/Qt_MainView.png
--------------------------------------------------------------------------------
/doc/porting/Qt_Numberpad.2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/doc/porting/Qt_Numberpad.2.png
--------------------------------------------------------------------------------
/doc/porting/Qt_Numberpad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/doc/porting/Qt_Numberpad.png
--------------------------------------------------------------------------------
/doc/porting/Qt_Options.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/doc/porting/Qt_Options.png
--------------------------------------------------------------------------------
/doc/porting/Qt_Waitnote.2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/doc/porting/Qt_Waitnote.2.png
--------------------------------------------------------------------------------
/doc/porting/Qt_Waitnote.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/doc/porting/Qt_Waitnote.png
--------------------------------------------------------------------------------
/doc/porting/SL_Highscores.2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/doc/porting/SL_Highscores.2.png
--------------------------------------------------------------------------------
/doc/porting/SL_Landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/doc/porting/SL_Landscape.png
--------------------------------------------------------------------------------
/doc/porting/SL_MainPage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/doc/porting/SL_MainPage.png
--------------------------------------------------------------------------------
/doc/porting/SL_Numberpad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/doc/porting/SL_Numberpad.png
--------------------------------------------------------------------------------
/doc/porting/SL_Splashscreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/doc/porting/SL_Splashscreen.png
--------------------------------------------------------------------------------
/doc/porting/SL_Waitnote.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/sudokumaster-wp/1207408de608b6d6a4f18b7c8bf2de649f6442ec/doc/porting/SL_Waitnote.png
--------------------------------------------------------------------------------