} //END (getEngine) /// <summary> /// Start the new game /// </summary> /// <param name="MS">Minesweeper settings class</param> private void NewGame(MinesSettings MS) { NumMines = MS.NumMines; //set mines number NumFlags = 0; //clear flags number Width = MS.FieldWidth; //set minefield width Height = MS.FieldHeight; //set minefield height UseQuestionMarks = MS.UseQuestionMarks; //to use questions or not to? Level3BV = 0; //clear difficulty level CurrentGameState = new CurrentGameStateInfo //set current game state { State = GameState.NewGame, //ready to begin the new game BombedMines = null, //clear bombed mines array WrongFlags = null, //clear wrong flags array NumMinesBombed = 0, //clear number of bombed mines NumWrongFlags = 0 //clear number of wrong flags }; //ENDCURRENTGAMESTATE (game state) Array.Resize(ref field, Width); //get memory for the minefield Array.Resize(ref markers, Width); //get memory for markers Array.Resize(ref state, Width); //get memory for cell states for (int i = 0; i < Width; i++) //moving through all the minefield columns { Array.Resize(ref field[i], Height); //get memory for the current column Array.Resize(ref markers[i], Height); //get memory for the current column's markers Array.Resize(ref state[i], Height); //get memory for the current column's cell's states } //ENDFOR (columns) GenerateField(); //generate the new minefield } //END (NewGame)
} //ENDTHIS (external access) #endregion #region Methods /// <summary> /// Minesweeper engine ctor /// </summary> /// <param name="MS">Minesweeper settings class</param> private MinesEngine(MinesSettings MS) { //ctor is private. MinesEngine is developped as a Singleton. //It is needed to prevent creation of several instances of game engine. //Because engine can be only one. NewGame(MS); //just start the new game with specified settings } //END (ctor)
} //END (FormSettings_FormClosing) #endregion #region Form controls events handlers /// <summary> /// Close button click event handler /// </summary> /// <param name="sender">Event sender</param> /// <param name="e">Event arguments</param> private void butClose_Click(object sender, EventArgs e) { if (rbNewbie.Checked) //if Newbie radiobutton is checked { formParent.MS = MinesSettings.setSettings( //set new game settings MinesSettings.Preset.Newbie, //preset: Newbie cbUseQuestionMarks.Checked); //questions: equals checkbox state } //ENDIF (newbie) else if (rbAdvanced.Checked) //if Advanced radiobutton is checked { formParent.MS = MinesSettings.setSettings( //set new game settings MinesSettings.Preset.Advanced, //preset: Advanced cbUseQuestionMarks.Checked); //questions: equals checkbox state } //END (advanced) else if (rbProfessional.Checked) //if Professional radiobutton is checked { formParent.MS = MinesSettings.setSettings( //set new game settings MinesSettings.Preset.Professional, //preset: Professional cbUseQuestionMarks.Checked); //questions: equals checkbox state } //END (professional) else if (rbCustom.Checked) //if Custom radiobutton is checked { formParent.MS = MinesSettings.setSettings( //set new game settings (int)nudWidth.Value, //width equals Width box (int)nudHeight.Value, //height equals Height box (uint)nudMines.Value, //mines equals Mines box cbUseQuestionMarks.Checked); //questions: equals checkbox state } //END (custom) else //if somehow no radiobutton is checked { formParent.MS = MinesSettings.setSettings(); //set default game settings } Close(); //close Settings form } //END (butClose_Click)
} //END (ctor) /// <summary> /// Settings form load event handler /// </summary> /// <param name="sender">Event sender</param> /// <param name="e">Event arguments</param> private void FormSettings_Load(object sender, EventArgs e) { switch (formParent.MS.CurrentPreset) //switch between presets { case MinesSettings.Preset.Newbie: //current preset is Newbie rbNewbie.Checked = true; //check Newbie radiobutton break; case MinesSettings.Preset.Advanced: //current preset is Advanced rbAdvanced.Checked = true; //check Advanced radiobutton break; case MinesSettings.Preset.Professional: //current preset is Professional rbProfessional.Checked = true; //check Professional radiobutton break; case MinesSettings.Preset.Custom: //current preset is Custom rbCustom.Checked = true; //check Custom radiobutton break; } //ENDSWITCH (presets) nudHeight.Value = formParent.MS.FieldHeight; //set Height box to the current mine field height nudWidth.Value = formParent.MS.FieldWidth; //set Width box to the current mine field width nudMines.Value = formParent.MS.NumMines; //set Mines box to the current mines number nudMines.Maximum = MinesSettings.GetMaxMines((int)nudWidth.Value, (int)nudHeight.Value); //set maximal value of the Mines box to appropriate for current mine field cbUseQuestionMarks.Checked = formParent.MS.UseQuestionMarks; //set use question marks check-box as in current settings } //END (FormSettings_Load)
} //END (ctor - by numeric values) /// <summary> /// Set default settings /// </summary> /// <returns>Unique instance of MineSettings class</returns> public static MinesSettings setSettings() { if (Instance == null) //if class instance is not created yet { lock (multyThreadLock) //lock static members of class { if (Instance == null) //if class instance is still not created { Instance = new MinesSettings(); //create unique instance of class with default settings } } //ENDLOCK (multy thread lock) } else //if class instance is already created { Instance.ChangeSettings(Preset.Newbie, true); //change settings to default } return(Instance); //return unique instance } //END (setSettings - default)
} //END (ctor) /// <summary> /// Gets unique instance of the MinesEngine with specified settings /// </summary> /// <param name="MS">Settings for the engine</param> /// <returns>Unique instance of the MinesEngine class</returns> public static MinesEngine getEngine(MinesSettings MS) { if (Instance == null) //if class instance is not created { lock (multyThreadLock) //begin lock section (to nobody could create instance at the same time) { if (Instance == null) //again: if class instance is not created { Instance = new MinesEngine(MS); //create unique class instance } } //ENDLOCK (lock section) } else //if class instance is already created { Instance.NewGame(MS); //just begin the new game for it } return(Instance); //return unique class instance } //END (getEngine)
} //END (setSettings - default) /// <summary> /// Set default settings by preset /// </summary> /// <returns>Unique instance of MineSettings class</returns> public static MinesSettings setSettings(Preset preset, bool useQuestionMarks) { if (Instance == null) //if class instance is not created { lock (multyThreadLock) //lock static members of class { if (Instance == null) //if class instance is still not created { Instance = new MinesSettings(preset, useQuestionMarks); //create instance with specified preset } } //ENDLOCK (multy thread lock) } else //if class instance is already created { Instance.ChangeSettings(preset, useQuestionMarks); //change its settings according the specified preset } return(Instance); //return unique instance of class } //END (setSettings - by preset)
} //END (setSettings - by preset) /// <summary> /// Set default settings by numeric parameters /// </summary> /// <returns>Unique instance of MineSettings class</returns> public static MinesSettings setSettings(int width, int height, uint mines, bool useQuestionMarks) { if (Instance == null) //if class instance is not created { lock (multyThreadLock) //lock static members of the class { if (Instance == null) //if class instance is still not created { Instance = new MinesSettings(width, height, mines, useQuestionMarks); //create new instance using specified values } } //ENDLOCK (multy thread lock) } else //if class instance is already created { Instance.ChangeSettings(width, height, mines, useQuestionMarks); //change its settings according to specified values } return(Instance); //return unique instance of the class } //END (setSettings - by numeric values)
} //END (ctor) /// <summary> /// Main form load handler /// </summary> /// <param name="sender">Sender of the form load event</param> /// <param name="e">Form load event args</param> private void FormMineSweeper_Load(object sender, EventArgs e) { //loading game settings try //trying to load settings from the file { FileStream fs = new FileStream("minesettings.soap", FileMode.Open); //using SOAP-serialization SoapFormatter formatter = new SoapFormatter(); MS = (MinesSettings)formatter.Deserialize(fs); //disposing the file-stream fs.Dispose(); fs = null; } //ENDTRY catch (FileNotFoundException) //if game-settings file wasn't found { MS = MinesSettings.setSettings(); //loading default settings } //ENDCATCH (File not found) catch (SerializationException) //if something went wrong with serialization { MS = MinesSettings.setSettings(); //loading default settings } //ENDCATCH (Serialization) //loading game stats try //trying to load stats from the file { FileStream fs = new FileStream("minestats.soap", FileMode.Open); //using SOAP-serialization SoapFormatter formatter = new SoapFormatter(); MStats = (MinesStatistics)formatter.Deserialize(fs); //disposing the file-stream fs.Dispose(); fs = null; } //ENDTRY catch (FileNotFoundException) //if game-stats file wasn't found { MStats = MinesStatistics.getInstance(); //create empty stats } //ENDCATCH (File not found catch (SerializationException) //if something went wrong with serialization { MStats = MinesStatistics.getInstance(); //create empty stats } //ENDCATCH (Serialization) //setting styles for the form controls SetStyle(ControlStyles.UserPaint, true); //controls must be drawn by themselves (not by OS) SetStyle(ControlStyles.AllPaintingInWmPaint, true); //controls must ignore WM_ERASEBKGND message (reduces flicker) SetStyle(ControlStyles.OptimizedDoubleBuffer, true); //controls must be pre-drawn in buffer (reduces flicker) SetStyle(ControlStyles.ResizeRedraw, true); //controls must be re-drawn when their size changes UpdateStyles(); //force apply the new styles //creating prototype mine-field label (will be used in cloning all the minefield cells) flPrototype = new MineFieldLabel( new Size(CellSize, CellSize), //label size ilIconsField, //image list for label ClosedColor, //back color of the label Cell_Down, //mouse-down event handler Cell_Up, //mouse-up event handler MineFieldLabel_DoubleClick); //double-click event handler //generating all the cells for the maximal-field-dimensions by using prototype label Array.Resize(ref FL, MinesSettings.MaxWidth); //get memory for the field for(int i = 0; i < MinesSettings.MaxWidth; i++) //moving through all the columns { Array.Resize(ref FL[i], MinesSettings.MaxHeight); //get memory for the current field column for (int j = 0; j < MinesSettings.MaxHeight; j++) //moving through all the cells in current column FL[i][j] = flPrototype.GetNew(CellSize, i, j); //create new label for the current cell gbMineField.Controls.AddRange(FL[i]); //add current column of cells into the field } //ENDFOR (columns) //initialising the mine field InitialiseField(); } //END (FormMineSweeper_Load)
} //END (nudHeight_ValueChanged) /// <summary> /// Width box value changed event handler /// </summary> /// <param name="sender">Event sender</param> /// <param name="e">Event arguments</param> private void nudWidth_ValueChanged(object sender, EventArgs e) { nudMines.Maximum = MinesSettings.GetMaxMines((int)nudWidth.Value, (int)nudHeight.Value); //set maximal value for the Mines box } //END (nudWidth_ValueChanged)
} //END (getInstance) /// <summary> /// Calculate stats for the current game /// </summary> /// <param name="me">Engine of the current game</param> /// <param name="ms">Settings of the current game</param> /// <param name="askPN">Handler of the method that asks for a player name</param> public void CalcStats(MinesEngine me, MinesSettings ms, uint gameTime, AskPlayerName askPN) { DateTime curTime = DateTime.Now; //current date and time int index = -1; //index of the current preset in the stats array if (me.CurrentGameState.State != MinesEngine.GameState.Loose && //if game is not lost me.CurrentGameState.State != MinesEngine.GameState.Win) //AND game is not won { return; //do nothing and just return } uint level3BV = me.Level3BV; //get difficulty level of the game from the engine switch (ms.CurrentPreset) //switch between game presets { case MinesSettings.Preset.Newbie: index = 0; break; //for Newbie index is zero case MinesSettings.Preset.Advanced: index = 1; break; //for Advanced index is 1 case MinesSettings.Preset.Professional: index = 2; break; //for Professional index is 2 case MinesSettings.Preset.Custom: //for Custom we will not add stats by preset // but we will try to add a record to the top by difficulty level if (me.CurrentGameState.State == MinesEngine.GameState.Win) //if game is won { AddToTop(3, curTime, gameTime, level3BV, askPN); //try to add record to top list } return; //return default: return; //if somehow preset in game settings is invalid do nothing and return } //ENDSWITCH (preset) StatsByPreset[index].TotalGames++; //increase total games played for the current preset if (me.CurrentGameState.State == MinesEngine.GameState.Win) //if game is won { if (gameTime > StatsByPreset[index].LongestGameTime) //if current game duration is more than the saved in stats one { StatsByPreset[index].LongestGameTime = gameTime; //save current game duration } StatsByPreset[index].WinGames++; //increase won games counter StatsByPreset[index].WinPercent = StatsByPreset[index].WinGames / (double)StatsByPreset[index].TotalGames * 100.0; //calculate the new win-percentage StatsByPreset[index].WinStreak++; //increase win streak counter if (StatsByPreset[index].LooseStreak > StatsByPreset[index].MaxLooseStreak) //if current loose streak is longer than the saved one { StatsByPreset[index].MaxLooseStreak = StatsByPreset[index].LooseStreak; //save the current loose streak as the maximal } StatsByPreset[index].LooseStreak = 0; //reset current loose streak StatsByPreset[index].FastestGameTime = AddToTop(index, curTime, gameTime, level3BV, askPN); //try to add current game to top } //ENDIF (game is won) else //if game is LOST { if (StatsByPreset[index].WinStreak > StatsByPreset[index].MaxWinStreak) //if current win streak is longer than the saved one { StatsByPreset[index].MaxWinStreak = StatsByPreset[index].WinStreak; //save current win streak as the maximal } StatsByPreset[index].WinStreak = 0; //reset current win streak StatsByPreset[index].LooseStreak++; //increase the loose streak counter } //ENDELSE (game is lost) } //END (CalcStats)