Sudoku/Complete/Models/BoardModel.cs

551 lines
18 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;
namespace SudokuWeek4.Models
{
public partial class BoardModel
{
// Define an array to hold the bigCells
private LargeCell[,] bigCells;
// Define an array to hold the smallCells
private LittleCell[,] smallCells;
// Define the current X position
private int currentX = 0;
// Define the current Y position
private int currentY = 0;
// This property will get or set the cheatmode
public static Boolean CheatMode { get; set; }
// Define the gameFieldSize
public static int GameFieldSize;
// Define the gameFieldCount
public static int GameFieldCount;
// Define the GameField
private UniformGrid _gameField = new UniformGrid();
// Define the sodukuGame (if the DLL is enabled)
private Sudoku.IGame _sudokuGame;
// Create a dictionary that holds the cheatDicisions
private readonly Dictionary<IntegerPoint, int> _cheatDictionary = new Dictionary<IntegerPoint, int>();
// Define if the sudokuDLL is used
private Boolean _sudokuDLL = true;
// Define a gameFinished boolean
private Boolean _gameFinished = false;
private int _inputCount;
public BoardModel()
{
// Just keep smiling ^_^
}
// This property will get the gameField
public UniformGrid GameField
{
get { return _gameField; }
}
// This method will set the cheatmode
public void Cheat(Boolean cheatMode = false)
{
CheatMode = cheatMode;
RefreshAllCells();
}
// Refresh all the cells on the gameField
public void RefreshAllCells()
{
for (int x = 0; x < GameFieldSize; x++)
{
for (int y = 0; y < GameFieldSize; y++)
{
smallCells[x, y].Refresh();
}
}
}
public void Reset(int gameFieldSize = 3)
{
// Clear the gameField
GameField.Children.Clear();
// Reset the inputCount
_inputCount = 0;
// Reset the game Finished Boolean
_gameFinished = false;
// Set the boardCount
GameFieldCount = gameFieldSize;
// Set the boardSize
GameFieldSize = GameFieldCount * GameFieldCount;
// Create all the gameField columns (depending on GameFieldSize)
GameField.Columns = GameField.Rows = GameFieldCount;
// Create all the big cells (2D array of gamefield size)
bigCells = new LargeCell[GameFieldCount, GameFieldCount];
// Create the gameField
for (int i = 0; i < GameFieldSize; i++)
{
// Create a new border
Border border = new Border();
// Set the border thickness
border.BorderThickness = new Thickness(1);
// Set the borderBrush color
border.BorderBrush = Brushes.Blue;
// Add the border to the gameField
GameField.Children.Add(border);
// Create a bigCell
LargeCell bigCell = new LargeCell();
// Add the bigCell as a child to the created border
border.Child = bigCell;
// Set the bigcells relative to the gameField size
bigCells[i % GameFieldCount, i / GameFieldCount] = bigCell;
}
// Load all the small cells
LoadAllSmallCells();
// Clear all the filled in values
_cheatDictionary.Clear();
}
// Load all the smallCells
private void LoadAllSmallCells()
{
// Check if the selected gameMode is for 3 x 3 (if not we need to disable the sudoku DLL)
if (GameFieldCount != 3)
{
_sudokuDLL = false;
MessageBox.Show("This gamemode cannot be played with the sudoku DLL. SudokuDLL unloaded");
}
// If the sudoDLL is used we need to create a game
if (SudokuDLL)
{
try
{
_sudokuGame = new Sudoku.Game();
_sudokuGame.create();
Console.WriteLine("Sudoku DLL loaded successfully");
}
catch (Exception ex)
{
Console.WriteLine("Error while loading sudoku DLL: \n\n" + ex.Message + "\n\n");
SudokuDLL = false;
}
}
// Create the new smallCells
smallCells = new LittleCell[GameFieldSize, GameFieldSize];
// Reset the current X and Y position (Selected cell)
currentX = 0;
currentY = 0;
for (int x = 0; x < GameFieldSize; x++)
{
for (int y = 0; y < GameFieldSize; y++)
{
smallCells[x, y] = bigCells[x / GameFieldCount, y / GameFieldCount][x % GameFieldCount, y % GameFieldCount];
smallCells[x, y].X = x;
smallCells[x, y].Y = y;
}
}
// If the sudoku DLL is loaded we need to fill in the gameField
if (SudokuDLL)
{
FillSudokuFields();
}
/* // Check array contents
Console.WriteLine("Soduku DLL contents: \n\n -------------------------------\n\n");
for (int x = 1; x <= GameFieldSize; x++)
{
for (int y = 1; y <= GameFieldSize; y++)
{
int sudokuValue;
_sudokuGame.get(x, y, out sudokuValue);
Console.Write(sudokuValue);
if (sudokuValue == -1)
{
Console.WriteLine();
}
}
} */
smallCells[0, 0].Selected = true;
}
// This method will fill in all the sudokuFields
private void FillSudokuFields()
{
for (int x = 0; x < GameFieldSize; x++)
{
for (int y = 0; y < GameFieldSize; y++)
{
try
{
int sudokuValue;
_sudokuGame.get(x + 1, y + 1, out sudokuValue);
// Check if the sudoku value is not an empty (0) value
if (sudokuValue > 0)
{
// Set the specified value
SetNumber(x, y, sudokuValue, false, true);
_cheatDictionary[new IntegerPoint(x, y)] = sudokuValue;
}
}
catch (Exception ex)
{
Console.WriteLine("Error while generating the sudoku field when using the Sudoku DLL: \n\n" + ex.Message + "\n\n");
break;
}
}
}
}
// This method will solve the sudoku puzzle (partially)
public void solvePuzzle(int cellsOver = 0)
{
for (int i = _inputCount; i < smallCells.Length - cellsOver; i++)
{
ShowHint();
System.Console.WriteLine(i);
}
}
// This method will get the current clicked cell
public void MouseClicked(object sender, MouseEventArgs click)
{
/* if (sender != null)
{
UniformGrid _grid = sender as UniformGrid;
int _row = (int)_grid.GetValue(Grid.RowProperty);
int _column = (int)_grid.GetValue(Grid.ColumnProperty);
MessageBox.Show(string.Format("Grid clicked at column {0}, row {1}", _column, _row));
} */
}
// This method will check what to do if a key is pressed
public void KeyPressed(KeyEventArgs e)
{
// Check if the keys left, right, up or down are pressed
if (e.Key == Key.Left || e.Key == Key.Right || e.Key == Key.Up || e.Key == Key.Down)
{
// Disable the current selected cell
smallCells[currentX, currentY].Selected = false;
// Check which key is pressed and do the appropriate action
switch (e.Key)
{
case Key.Left:
currentX = (currentX + GameFieldSize - 1) % GameFieldSize;
break;
case Key.Right:
currentX = (currentX + 1) % GameFieldSize;
break;
case Key.Up:
currentY = (currentY + GameFieldSize - 1) % GameFieldSize;
break;
case Key.Down:
currentY = (currentY + 1) % GameFieldSize;
break;
}
// Set the current (new) cell to be selected
smallCells[currentX, currentY].Selected = true;
}
// Check if the numberkeys where pressed
if ((e.Key >= Key.D1 && e.Key <= Key.D9) || (e.Key >= Key.NumPad1 && e.Key <= Key.NumPad9))
{
int number = 0;
if (e.Key >= Key.D1 && e.Key <= Key.D9)
{
number = e.Key - Key.D0;
}
else
{
number = e.Key - Key.NumPad0;
}
IntegerPoint cellPoint = new IntegerPoint(currentX, currentY);
// Check if the cheatmode contains the key to be pressed
if (_cheatDictionary.ContainsKey(cellPoint))
{
// Remove the userinput at the specified location
_cheatDictionary.Remove(cellPoint);
}
// Check if the cheatMode does not contains the pressed numberKey and if the current has a candidate that can be selected
if (!_cheatDictionary.ContainsKey(cellPoint) && smallCells[currentX, currentY].CanSet(number))
{
if (SetNumber(currentX, currentY, number))
{
_cheatDictionary[cellPoint] = number;
}
}
}
if (e.Key == Key.Delete && !smallCells[currentX, currentY].ReadOnly)
{
// Add the option at the specified column
foreach (IntegerPoint cellPoint in GetPointsInColumn(currentX))
{
smallCells[cellPoint.X, cellPoint.Y].AddOption(smallCells[currentX, currentY].Determined);
}
// Add the option at the specified row
foreach (IntegerPoint cellPoint in GetPointsInRow(currentY))
{
smallCells[cellPoint.X, cellPoint.Y].AddOption(smallCells[currentX, currentY].Determined);
}
// Add the option at the specified BigCell
foreach (IntegerPoint cellPoint in GetPointsInParentBigCell(currentX, currentY))
{
smallCells[cellPoint.X, cellPoint.Y].AddOption(smallCells[currentX, currentY].Determined);
}
smallCells[currentX, currentY].RemoveNumber(smallCells[currentX, currentY].Determined);
}
}
// Set the specified number
private Boolean SetNumber(int x, int y, int number, Boolean hint = false, Boolean readOnly = false)
{
if (!_gameFinished)
{
if (number < 1 || number > GameFieldSize || smallCells[x, y].ReadOnly)
{
Console.WriteLine("Cannot set the value: The value is less or greater than the GameField size. (Or the cell is readOnly)\nActual Value: " + number + "\n");
return false;
}
// Remove the option at the specified column
foreach (IntegerPoint cellPoint in GetPointsInColumn(x))
{
smallCells[cellPoint.X, cellPoint.Y].RemoveOption(number);
}
// Remove the option at the specified row
foreach (IntegerPoint cellPoint in GetPointsInRow(y))
{
smallCells[cellPoint.X, cellPoint.Y].RemoveOption(number);
}
// Remove the option at the specified BigCell
foreach (IntegerPoint cellPoint in GetPointsInParentBigCell(x, y))
{
smallCells[cellPoint.X, cellPoint.Y].RemoveOption(number);
}
// Set the userInput on the specified cell
smallCells[x, y].MakeDecision(number, hint, readOnly);
_inputCount++;
if (_sudokuDLL)
{
int canAdapt;
_sudokuGame.set(x + 1, y + 1, number, out canAdapt);
}
if (CheatMode)
{
// Check which numbers are still posible to set
FindCheatNumbers(GetPointsInColumn(x));
FindCheatNumbers(GetPointsInRow(y));
FindCheatNumbers(GetPointsInParentBigCell(x, y));
}
// Check if we got a winner
if (_inputCount == smallCells.Length)
{
if (CheckWinner())
{
_gameFinished = true;
MessageBox.Show("Game finished!");
return false;
}
}
return true;
}
return false;
}
// Show a hint
public void ShowHint()
{
if (_sudokuDLL)
{
int hintPossible, value, x, y;
_sudokuGame.hint(out hintPossible, out x, out y, out value);
if (hintPossible == 1)
{
// Todo: We need to decrease the inputCount if the hint overrides a value that was already set
SetNumber(--x, --y , value, true);
}
}
}
// Save the game
public void Save()
{
if (_sudokuDLL)
{
int write;
_sudokuGame.write(out write);
if (write == 1)
{
MessageBox.Show("File has been saved succesfully");
RefreshAllCells();
}
}
}
// Load the game
public void Load()
{
if (_sudokuDLL)
{
int read;
_sudokuGame.read(out read);
if (read == 1)
{
MessageBox.Show("File has been loaded succesfully");
RefreshAllCells();
}
}
}
public Boolean CheckWinner()
{
int endGame;
_sudokuGame.isValid(out endGame);
return endGame == 1;
}
// Find the numbers that can be set (Cheat mode)
private void FindCheatNumbers(List<IntegerPoint> optionPoints)
{
try
{
Dictionary<int, int> dictionaryCount = new Dictionary<int, int>();
for (int i = 1; i <= GameFieldSize; i++)
{
dictionaryCount[i] = 0;
}
foreach (IntegerPoint optionPoint in optionPoints)
{
foreach (int option in smallCells[optionPoint.X, optionPoint.Y].Options)
{
dictionaryCount[option]++;
}
}
foreach (int cell in (from keys in dictionaryCount.Keys where dictionaryCount[keys] == 1 select keys).ToArray())
{
foreach (IntegerPoint cellPoint in optionPoints)
{
if (smallCells[cellPoint.X, cellPoint.Y].CanSet(cell))
smallCells[cellPoint.X, cellPoint.Y].SetPossible(cell);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
// Get the current points that where set in the bigCell
private static List<IntegerPoint> GetPointsInParentBigCell(int x, int y)
{
int x0 = x - x % GameFieldCount;
int y0 = y - y % GameFieldCount;
List<IntegerPoint> cellPoints = new List<IntegerPoint>();
for (int i = 0; i < GameFieldSize; i++)
{
cellPoints.Add(new IntegerPoint(x0 + i % GameFieldCount, y0 + i / GameFieldCount));
}
return cellPoints;
}
// Get the current points that where set in the current row
private static List<IntegerPoint> GetPointsInRow(int y)
{
List<IntegerPoint> cellPoints = new List<IntegerPoint>();
for (int i = 0; i < GameFieldSize; i++)
{
cellPoints.Add(new IntegerPoint(i, y));
}
return cellPoints;
}
// Create a property to check if the sudokuDLL is used
public Boolean SudokuDLL
{
get { return _sudokuDLL; }
set
{
_sudokuDLL = value;
Reset(GameFieldCount);
}
}
// Get the current points that where set in the current column
private static List<IntegerPoint> GetPointsInColumn(int x)
{
List<IntegerPoint> cellPoints = new List<IntegerPoint>();
for (int i = 0; i < GameFieldSize; i++)
{
cellPoints.Add(new IntegerPoint(x, i));
}
return cellPoints;
}
}
}