El post de hoy va sobre la generación de un sudoku.
Para el que no lo sepa, un sudoku consta de una matriz de 9 cuadrados de 3×3, los cuales contienen 9 números del 1 al 9 cada uno.
La característica principal del sudoku, es que no se repite ningún número en las filas, columnas y cuadrados de 3×3. Un ejemplo de ello, sería el siguiente:

Podéis comprobar que en todas las filas, se encuentran los números del 1 al 9
los cuales no se repiten; si analizamos las columnas, ocurre lo mismo y si lo hacemos con los cuadrados de 3×3, pasa igual.Para generar una matriz completa de sudoku, debemos crear un método que inicie una secuencia y en el caso de que no sea idónea, es decir que se repita algún número o que no se pueda incluir, debe iniciarla de nuevo hasta encontrar una que lo sea, pero para ello, debemos ayudarle a escoger el buen camino y que el número de repeticiones sea el menor posible.Llevándolo a la práctica, el método que he utilizado para generar la matriz es el siguiente:Lo primero de todo, es optimizar el rendimiento de nuestra aplicación y para hacerlo, nos ayudaremos del objeto
List
, el cual permite añadir (
Add
), eliminar(
remove
) y obtener un objeto mediante su índice. En él, añado los números del 1 al 9 y obteniendo un número aleatorio desde 0 al número de objetos de la lista, puedo ir extrayendo como si de un bombo se tratara cada número, así nos aseguramos que los números no se repiten.En primer lugar, esta primera secuencia la creamos por filas.
En cada una de las posiciones, elimino los elementos que están a la izquierda o en su misma fila, elimino los que están por encima o los que pertenecen a la misma columna y por último, elimino los que se encuentran en el mismo cuadrado de 3×3.
De estos números candidatos, debemos elegir el más idóneo, pero… dejando que el azar haga su trabajo y para realizar esto, calculo la probabilidad de incluir el número en la fila o lo que es lo mismo, determinar el número de veces que puede incluirse en las siguientes posiciones. Recordemos que la probabilidad de un suceso es igual al número de casos favorables o el número de casos que cumplen con nuestro objetivo, dividido entre el número de casos totales o posibles. En nuestro caso, si estamos decidiendo obtener el número central del cuadrado, el número con coordenadas 4,4 (en la imagen equivale al número 7 central, las coordenadas van desde 0 hasta 8), la probabilidad de incluir el número 7 en las siguientes columnas sería de 3/5, es decir 3 columnas en las que es posible incluir el número 7 de las 5 que quedan.

Pongamos un ejemplo

La posición con coordenadas 4,5, tiene como números candidatos el 1 y el 6; el 1 tiene un 50 % de probabilidad de colocarse en las siguientes columnas, (puede incluirse en la columna 5 y 8 sobre 4 columnas) y el 6 un 75% (puede incluirse en la columna 5,6 y 8 sobre 4 columnas), por tanto lo que hacemos es «incentivar» a los que tienen menos probabilidad con un número igual a 1-probabilidad o probabilidad de no ocurrencia, en este caso creo un conjunto con un número igual a la probabilidad de no ocurrencia multiplicado por 10; en el ejemplo creo un conjunto de 0.5 x 10=5 números 1 y 0.25 x 10=2,5 o 2 números 6 {1,1,1,1,1,6,6}, seleccionamos un número al azar y ya tenemos nuestra elección, el número 1 con más probabilidad de incluirse que el 6, pero ambos están en el bombo y así no descartamos futuras combinaciones válidas.
La secuencia va aumentando hasta que, o no tiene ningún candidato en alguna de las posiciones y por tanto debe comenzar de nuevo o llega al final de la secuencia con una combinación de números adecuada gracias a nuestra ayuda probabilística.
Esto aplicado a código, lo hago con C# y WPF, me encanta escribir y no escribir código! me encanta escribir clases independientes que no tengan ninguna relación con la interfaz gráfica y me encanta crear formularios sin código (C# o VB .NET).
Como clase ViewModel, la clase SudokuManager
, las dos implementan la interfaz INotifiPropertyChanged
para que podamos enlazar los datos de nuestro código XAML y de las cuales podemos mostrarlas mediante el siguiente diagrama de clases

Creo una clase llamada Point
para cada posición de la matriz con las coordenadas, el valor de la casilla, una propiedad de solo lectura para calcular el cudrado de 3×3 al que pertenece y si la posición es visible para ocultarla en el futuro y poder calcularla.
En la clase SudokuManager
, tenemos una propiedad que es una colección de estas 81 posiciones, dos propiedades una para obtener el tiempo de generación en milisegundos y otra para ver el número de iteraciones que ha necesitado para generar el sudoku completo y una vez que este ha sido creado correctamente, la propiedad IsGood
pasa a True.
Como métodos públicos, Start
para generar una matriz nueva y ToString para obtener la lista completa textualmente. Tengo otros métodos para ocultar los números en base a la dificultad y poder jugar o poder calcular el Sudoku automáticamente, pero eso quedará para otro día.
Como maquinaria interna o métodos privados, el principal es el método setSelection
al cual le pasamos como parámetro el objeto Point o cuadrícula de la matriz, de la lista inicial del 1 al 9, le quita los elementos de su izquierda o de su misma fila, los elementos superiores o de su columna y los elementos de su cuadrícula de 3×3, una vez hecho esto, solo quedarían los números candidatos y llamaría al método getSelection
, el cual calcula la probabilidad que hemos explicado y obtiene el número definitivo que se le asignará a la posición.
Una vez hecho esto, vamos a ver si el rendimiento es adecuado, ya que imaginad que para generar una matriz, necesitara 5 segundos, algo inaceptable para los tiempos que corren, por lo que he realizado varias muestras de 50 generaciones y de todas ellas, obtengo un promedio de 53,58 iteraciones y un promedio de 192 milisegundos por cada una, estando el valor mínimo de 1 iteración y 5 ms (con un I5 2400, que no es un I7 6500!) y unos valores máximos de 165 iteraciones y medio segundo de los cuales hay que decir que tienen una probabilidad de salir de un 2%, o una probabilidad de un 16% de superar las 100 iteraciones y los 350ms.
Bueno, para ser el primero, creo que no está mal, seguro que se pueden arañar algún milisegundo en alguna operación.
En cuanto al XAML, he creado un control de usuario para que no sea muy extenso el código de la página principal, al cual le pasamos como DataContext la clase SudokuManager y a cada posición, le asignamos como DataContext el objeto Point que le pertenece, de modo que desde el estilo, podemos hacer un Binding a sus propiedades, un poco de código como muestra
<UserControl x:Class="Ramanujan.Matriz"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ramanujan"
mc:Ignorable="d"
d:DesignHeight="490" d:DesignWidth="490">
<Grid x:Name="grid">
<Grid.Resources>
<!-- CONVERTER -->
<local:BoolToVisibleConverter x:Key="b2vc"/>
<!-- ESTILO DE TEXTBLOCK -->
<Style x:Key="textBlockStyle" TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="TextAlignment" Value="Center"/>
<Setter Property="FontSize" Value="24"/>
<Setter Property="Visibility" Value="{Binding IsVisible,Converter={StaticResource b2vc}}"/>
<Setter Property="Text" Value="{Binding Value}"/>
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="20*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="20*"/>
</Grid.RowDefinitions>
<Border Grid.Column="1" Grid.Row="1" Grid.RowSpan="9" Grid.ColumnSpan="9" BorderBrush="WhiteSmoke" BorderThickness="2"/>
<Border Grid.Column="1" Grid.Row="1" Grid.RowSpan="3" Grid.ColumnSpan="3" BorderBrush="WhiteSmoke" BorderThickness="1"/>
<Border Grid.Column="4" Grid.Row="1" Grid.RowSpan="3" Grid.ColumnSpan="3" BorderBrush="WhiteSmoke" BorderThickness="1"/>
<Border Grid.Column="7" Grid.Row="1" Grid.RowSpan="3" Grid.ColumnSpan="3" BorderBrush="WhiteSmoke" BorderThickness="1"/>
<Border Grid.Column="1" Grid.Row="4" Grid.RowSpan="3" Grid.ColumnSpan="3" BorderBrush="WhiteSmoke" BorderThickness="1"/>
<Border Grid.Column="4" Grid.Row="4" Grid.RowSpan="3" Grid.ColumnSpan="3" BorderBrush="WhiteSmoke" BorderThickness="1"/>
<Border Grid.Column="7" Grid.Row="4" Grid.RowSpan="3" Grid.ColumnSpan="3" BorderBrush="WhiteSmoke" BorderThickness="1"/>
<Border Grid.Column="1" Grid.Row="7" Grid.RowSpan="3" Grid.ColumnSpan="3" BorderBrush="WhiteSmoke" BorderThickness="1"/>
<Border Grid.Column="4" Grid.Row="7" Grid.RowSpan="3" Grid.ColumnSpan="3" BorderBrush="WhiteSmoke" BorderThickness="1"/>
<Border Grid.Column="7" Grid.Row="7" Grid.RowSpan="3" Grid.ColumnSpan="3" BorderBrush="WhiteSmoke" BorderThickness="1"/>
<TextBlock x:Name="textBlock00" DataContext="{Binding Points[0]}" Grid.Column="1" Grid.Row="1" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock01" DataContext="{Binding Points[1]}" Grid.Column="2" Grid.Row="1" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock02" DataContext="{Binding Points[2]}" Grid.Column="3" Grid.Row="1" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock03" DataContext="{Binding Points[3]}" Grid.Column="4" Grid.Row="1" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock04" DataContext="{Binding Points[4]}" Grid.Column="5" Grid.Row="1" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock05" DataContext="{Binding Points[5]}" Grid.Column="6" Grid.Row="1" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock06" DataContext="{Binding Points[6]}" Grid.Column="7" Grid.Row="1" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock07" DataContext="{Binding Points[7]}" Grid.Column="8" Grid.Row="1" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock08" DataContext="{Binding Points[8]}" Grid.Column="9" Grid.Row="1" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock10" DataContext="{Binding Points[9]}" Grid.Column="1" Grid.Row="2" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock11" DataContext="{Binding Points[10]}" Grid.Column="2" Grid.Row="2" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock12" DataContext="{Binding Points[11]}" Grid.Column="3" Grid.Row="2" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock13" DataContext="{Binding Points[12]}" Grid.Column="4" Grid.Row="2" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock14" DataContext="{Binding Points[13]}" Grid.Column="5" Grid.Row="2" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock15" DataContext="{Binding Points[14]}" Grid.Column="6" Grid.Row="2" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock16" DataContext="{Binding Points[15]}" Grid.Column="7" Grid.Row="2" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock17" DataContext="{Binding Points[16]}" Grid.Column="8" Grid.Row="2" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock18" DataContext="{Binding Points[17]}" Grid.Column="9" Grid.Row="2" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock20" DataContext="{Binding Points[18]}" Grid.Column="1" Grid.Row="3" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock21" DataContext="{Binding Points[19]}" Grid.Column="2" Grid.Row="3" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock22" DataContext="{Binding Points[20]}" Grid.Column="3" Grid.Row="3" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock23" DataContext="{Binding Points[21]}" Grid.Column="4" Grid.Row="3" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock24" DataContext="{Binding Points[22]}" Grid.Column="5" Grid.Row="3" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock25" DataContext="{Binding Points[23]}" Grid.Column="6" Grid.Row="3" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock26" DataContext="{Binding Points[24]}" Grid.Column="7" Grid.Row="3" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock27" DataContext="{Binding Points[25]}" Grid.Column="8" Grid.Row="3" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock28" DataContext="{Binding Points[26]}" Grid.Column="9" Grid.Row="3" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock30" DataContext="{Binding Points[27]}" Grid.Column="1" Grid.Row="4" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock31" DataContext="{Binding Points[28]}" Grid.Column="2" Grid.Row="4" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock32" DataContext="{Binding Points[29]}" Grid.Column="3" Grid.Row="4" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock33" DataContext="{Binding Points[30]}" Grid.Column="4" Grid.Row="4" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock34" DataContext="{Binding Points[31]}" Grid.Column="5" Grid.Row="4" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock35" DataContext="{Binding Points[32]}" Grid.Column="6" Grid.Row="4" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock36" DataContext="{Binding Points[33]}" Grid.Column="7" Grid.Row="4" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock37" DataContext="{Binding Points[34]}" Grid.Column="8" Grid.Row="4" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock38" DataContext="{Binding Points[35]}" Grid.Column="9" Grid.Row="4" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock40" DataContext="{Binding Points[36]}" Grid.Column="1" Grid.Row="5" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock41" DataContext="{Binding Points[37]}" Grid.Column="2" Grid.Row="5" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock42" DataContext="{Binding Points[38]}" Grid.Column="3" Grid.Row="5" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock43" DataContext="{Binding Points[39]}" Grid.Column="4" Grid.Row="5" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock44" DataContext="{Binding Points[40]}" Grid.Column="5" Grid.Row="5" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock45" DataContext="{Binding Points[41]}" Grid.Column="6" Grid.Row="5" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock46" DataContext="{Binding Points[42]}" Grid.Column="7" Grid.Row="5" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock47" DataContext="{Binding Points[43]}" Grid.Column="8" Grid.Row="5" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock48" DataContext="{Binding Points[44]}" Grid.Column="9" Grid.Row="5" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock50" DataContext="{Binding Points[45]}" Grid.Column="1" Grid.Row="6" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock51" DataContext="{Binding Points[46]}" Grid.Column="2" Grid.Row="6" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock52" DataContext="{Binding Points[47]}" Grid.Column="3" Grid.Row="6" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock53" DataContext="{Binding Points[48]}" Grid.Column="4" Grid.Row="6" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock54" DataContext="{Binding Points[49]}" Grid.Column="5" Grid.Row="6" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock55" DataContext="{Binding Points[50]}" Grid.Column="6" Grid.Row="6" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock56" DataContext="{Binding Points[51]}" Grid.Column="7" Grid.Row="6" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock57" DataContext="{Binding Points[52]}" Grid.Column="8" Grid.Row="6" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock58" DataContext="{Binding Points[53]}" Grid.Column="9" Grid.Row="6" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock60" DataContext="{Binding Points[54]}" Grid.Column="1" Grid.Row="7" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock61" DataContext="{Binding Points[55]}" Grid.Column="2" Grid.Row="7" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock62" DataContext="{Binding Points[56]}" Grid.Column="3" Grid.Row="7" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock63" DataContext="{Binding Points[57]}" Grid.Column="4" Grid.Row="7" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock64" DataContext="{Binding Points[58]}" Grid.Column="5" Grid.Row="7" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock65" DataContext="{Binding Points[59]}" Grid.Column="6" Grid.Row="7" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock66" DataContext="{Binding Points[60]}" Grid.Column="7" Grid.Row="7" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock67" DataContext="{Binding Points[61]}" Grid.Column="8" Grid.Row="7" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock68" DataContext="{Binding Points[62]}" Grid.Column="9" Grid.Row="7" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock70" DataContext="{Binding Points[63]}" Grid.Column="1" Grid.Row="8" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock71" DataContext="{Binding Points[64]}" Grid.Column="2" Grid.Row="8" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock72" DataContext="{Binding Points[65]}" Grid.Column="3" Grid.Row="8" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock73" DataContext="{Binding Points[66]}" Grid.Column="4" Grid.Row="8" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock74" DataContext="{Binding Points[67]}" Grid.Column="5" Grid.Row="8" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock75" DataContext="{Binding Points[68]}" Grid.Column="6" Grid.Row="8" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock76" DataContext="{Binding Points[69]}" Grid.Column="7" Grid.Row="8" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock77" DataContext="{Binding Points[70]}" Grid.Column="8" Grid.Row="8" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock78" DataContext="{Binding Points[71]}" Grid.Column="9" Grid.Row="8" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock80" DataContext="{Binding Points[72]}" Grid.Column="1" Grid.Row="9" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock81" DataContext="{Binding Points[73]}" Grid.Column="2" Grid.Row="9" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock82" DataContext="{Binding Points[74]}" Grid.Column="3" Grid.Row="9" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock83" DataContext="{Binding Points[75]}" Grid.Column="4" Grid.Row="9" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock84" DataContext="{Binding Points[76]}" Grid.Column="5" Grid.Row="9" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock85" DataContext="{Binding Points[77]}" Grid.Column="6" Grid.Row="9" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock86" DataContext="{Binding Points[78]}" Grid.Column="7" Grid.Row="9" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock87" DataContext="{Binding Points[79]}" Grid.Column="8" Grid.Row="9" Style="{StaticResource textBlockStyle}"/>
<TextBlock x:Name="textBlock88" DataContext="{Binding Points[80]}" Grid.Column="9" Grid.Row="9" Style="{StaticResource textBlockStyle}"/>
</Grid>
en cuanto a la clase generadora del Sudoku, os dejo el código de generación, si alguien lo necesita, que no dude en solicitarme el proyecto que gustosamente se lo pasaré.
Os dejo un gif de como funciona. Un saludos amig@s

Pd: El espacio de nombres no tiene nada que ver con los sudokus, pero a cada proyecto de visual studio, le asigno un nombre de un ilustre matemático o físico y en este caso le ha tocado a Ramanujan.
/* ****************************************************************
* © JOAQUIN MARTINEZ RUS 2016
* PROYECTO: Clase generadora de Sudoku
* Archivo: SudokuManager.cs
* Descripción: Clase SudokuManager.
*
* Historial:
* 1. 09 sep 2016. Creación
*
* Comentarios: Genera un sudoku mediante iteraciones erróneas
*
*
*******************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;
namespace Ramanujan
{
public class SudokuManager : INotifyPropertyChanged
{
#region Constructor
public SudokuManager()
{
Points = new List();
}
#endregion
#region Properties and fields
public List Points { get; set; }
List initialCollection = new List();
List selection = new List();
public bool IsGood { get; set; }
private int iterations;
public int Iterations
{
get { return iterations; }
set
{
iterations = value;
OnPropertyChanged("Iterations");
}
}
private double miliseconds;
public double Miliseconds
{
get { return miliseconds; }
set
{
miliseconds = value;
OnPropertyChanged("Miliseconds");
OnPropertyChanged("Points");
}
}
#endregion
#region Events
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string _PropertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(_PropertyName));
}
}
#endregion
#region Public methods
public bool Start()
{
reset();
Iterations++;
foreach (var item in Points)
{
int number = setSelection(item);
if (number != -1)
{
item.Value = number;
item.IsVisible = true;
}
else
{
IsGood = false;
return IsGood;
}
}
IsGood = true;
return IsGood;
}
public string ToString()
{
string textOut = "";
foreach (var item in Points)
{
textOut += item.Value.ToString() + " ";
if (item.YCoor == 8)
{
textOut += "\n";
}
}
return textOut;
}
#endregion
#region Private Methods
private void reset()
{
initialCollection.Clear();
selection.Clear();
Points.Clear();
for (int i = 1; i < 10; i++)
{
initialCollection.Add(i);
}
setCoordinates();
}
private void setCoordinates()
{
for (int i = 0; i Points.Where(k => k.YCoor == y).Select(k => k.Value).Contains(n));
// eliminar los elementos cuya coordenada x sea igual que la del punto
var xx = Points.Where(k => k.XCoor == x && k.Value != 0);
selection.RemoveAll(n => Points.Where(k => k.XCoor == x).Select(k => k.Value).Contains(n));
// eliminar los elementos del cuadrado
var square = getFirstSquareNumber(Points.IndexOf(p));
selection.RemoveAll(n => square.Contains(n));
return getElection(p);
}
private KeyValuePair getProbability(int n, Point p)
{
int nColExistNumber = 0;
for (int i = p.YCoor + 1; i h.Value).First();
if (k == n)
{
nColExistNumber++;
}
}
}
double casosTotales = 9 - p.XCoor;
double probability = (casosTotales - nColExistNumber) / casosTotales;
return new KeyValuePair(n, probability);
}
private int getElection(Point p)
{
List values = new List();
foreach (var item in selection)
{
values.Add(getProbability(item, p));
}
List candidatosTemporales = new List();
foreach (var item in values)
{
if (item.Value == 1)
{
candidatosTemporales.Add(item.Key);
}
else
{
for (int i = 0; i 0 ? candidatosTemporales[index] : -1;
return r;
}
private List getFirstSquareNumber(int index)
{
List numbers = new List();
int restoX = index % 3;
int multiplo = (index / 9) % 3;
int first = index - restoX - 9 * multiplo;
int nextIndex = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
nextIndex = first + i * 9 + j;
if (Points[nextIndex].Value != 0)
{
numbers.Add(Points[nextIndex].Value);
}
}
}
return numbers;
}
#endregion
}
public class Point:INotifyPropertyChanged
{
public Point(int x, int y)
{
XCoor = x;
YCoor = y;
}
public int XCoor { get; set; }
public int YCoor { get; set; }
public int Value { get; set; }
private bool isVisible=true;
public bool IsVisible
{
get { return isVisible; }
set
{
isVisible = value;
OnPropertyChanged("IsVisible");
}
}
public int Square
{
get
{
int x = XCoor % 3;
int y = YCoor % 3;
return y + 3 * x ;
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string _PropertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(_PropertyName));
}
}
}
}