Sudoku II

Sudoku II

Hace tiempo escribí un artículo sobre un método de generación de sudokus en wakicode, ahora se me ha ocurrido obtener un método sencillo de resolución de sudoku. En esta primera parte mostraré como intentar resolverlos por pura lógica, con operaciones lógicas en pura esencia, sin necesidad de efectuar simulaciones por fuerza bruta o prueba error. Eso será más adelante cuando la lógica no tenga cabida.

El método que se me ha ocurrido lo hago del siguiente modo.

1.- Definiciones.

  • Ln. Capa de posición de un elemento n, se corresponde con un 1 si existe n (o es visible) y 0 si no es visible. En el ejemplo, podemos visualizar para un n=2, la matriz resultado de L2.
  • Bnc. Byte de columna para n. En el ejemplo B2c=110101111 que corresponde a un 1 si la columna tiene un 1 y 0 si la columna es ausente de 1.
  • Bnr. Byte de fila para n. En el ejemplo B2r=110110111 que corresponde (de arriba hacia abajo) a un 1 si la fila tiene un 1 y 0 si esta es ausente de 1.
  • Bns. Byte de bloque 3×3 para n. En el ejemplo B2s=101011111 que corresponde (de izquierda a derecha y de arriba abajo) a un 1 si el bloque 3×3 tiene un 1 y 0 si el bloque 3×3 es ausente de 1.
  • Mo. Matriz de elementos ocupados. Matriz 9×9 identificada por un 1 si la celda tiene un valor visible y un 0 si no lo tiene.
  • Mnc. Matriz de columnas. Operación OR de cada uno de los elementos de Mo con el bit correspondiente de Bnc. Mo OR Bnc
  • Mnr. Matriz de filas. Operación OR de cada uno de los elementos de Mnc con el bit correspondiente de Bnr. Mnc OR Bnr
  • Mns. Matriz de bloque. Operación OR de cada uno de los elementos de Mnr con Bns. Mnr OR Bns
Mnc. Mo OR Bnc. Los elementos de la derecha corresponden al resultado de la operación OR de los elementos de la izquierda del mismo color.

Una vez que conocemos los elementos participantes en el método pasamos a describir su desarrollo:

  1. Generar Mnc. Buscar en la matriz elementos únicos con valor 0. Esto significa que al realizar la operación OR del byte de columna con los elementos ocupados, queremos discriminar los elementos en los que únicamente pueden estar en esa posición. En el caso del ejemplo para n=2 no existen elementos únicos puesto que las columnas que tienen 0, tienen más de un 0. La columna 3ª tiene tres «0» y 5ª columna tiene dos «0».
  2. Generar Mnr. Buscar en la matriz elementos únicos con valor 0. En el caso del ejemplo para n=2 la matriz resultante si tiene elementos únicos puesto que las columnas que tienen 0, tienen más de un 0. La columna 3ª tiene tres «0» y la 5ª columna tiene dos «0».
    El caso de la figura indica que el lugar ocupado por los ceros únicos, es una celda ocupada en este caso por un 2
  3. Generar Mns. Buscar en la matriz elementos únicos con valor 0. En este caso se buscan elementos únicos en el bloque 3×3 y que para el caso concreto, ha coincidido con los elementos obtenidos de en Mnr.
    La matriz de la derecha corresponde con la operación OR del Byte y la matriz origen
  4. En cada generación de una nueva Matriz se realiza una búsqueda de estos elementos únicos. Solo en el caso de ubicaciones ambiguas en las que es posible ocupar dos celdas o tres o más celdas con 3 o más números posibles, habría que usar otros métodos de fuerza bruta o prueba y error hasta que no se me ocurra otro método lógico para este caso. Esto lo dejaremos para otro post.

Para terminar os dejo el diagrama de clases y cuando se me ocurra un método lógico de resolución del sudoku pondré el código en GitHub.

He aquí una muestra con C# y WPF resolviendo Sudokus generados aleatoriamente con 50 celdas ocultas. El algoritmo en la mayoría de los casos, para unas 50 celdas ocultas, no ha necesitado más de 7 iteraciones para resolver todos los elementos por lógica. Le he indicado que dependiendo del número de iteraciones cambie de color el número encontrado, del azul, verde, amarillo al rojo como el color de mayor iteración.

Suffled

Suffled
Kotlin tiene algunas funciones extendidas muy prácticas, entre otras la función shuffled(), pero y C# ¿tiene esta función?. la respuesta es no y ¿como lo haremos? Bien, he aquí una extensión que uso en mi código como extensión de listas.

public static class Extensions
    {
        public static List Shuffled(this List source)
        {
            var rnd = new Random();
            return source
           .Select(x => new { value = x, order = rnd.Next() })
           .OrderBy(x => x.order).Select(x => x.value).ToList();
        }
    }

Lo primero sería crear un objeto anónimo por cada elemento de la lista al cual le asignaremos un índice aleatorio; este índice será ordenado posteriormente, de ese modo obtendremos la lista en un orden distinto.

var list = new List() { "Negro", "Marrón", "Rojo", "Naranja", "Amarillo", "Verde", "Azul", "Violeta", "Gris", "Blanco" };
            Console.WriteLine(string.Join("\n",list.Suffled()));
            Console.ReadKey();

y el resultado


Verde
Azul
Marrón
Amarillo
Gris
Blanco
Negro
Rojo
Violeta
Naranja

Genetic Algorithm & Binding

Genetic Algorithm & Binding
Ya que sabemos como implementar un algoritmo genético básico con la Interface de Waki, vamos a usar el algoritmo genético de ordenamiento del tablero de ajedrez del post Interface. Algoritmo Genético II y crearemos una interfaz gráfica con WPF y como no, aplicaremos lo que sabemos con Binding.
Lo primero que vamos a hacer es que la clase ChessBoard implemente la interfaz INotifyPropertyChanged del namespace System.ComponentModel. Una vez hecho esto, añadimos el evento public event PropertyChangedEventHandler PropertyChanged y creamos un método que realice la llamada. Ahora que ya podemos notificar cambios en las propiedades, le incluimos una llamada a este método desde la propiedad BestParent, es decir, cada vez que se encuentre una solución parcial en el algoritmo queremos que actualice la pantalla, para ello cambiamos también la propiedad Bestparent por su equivalente de propiedad completa.

public event PropertyChangedEventHandler PropertyChanged;

public void OnPropertyChanged(string property)
{
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
private List bestparent;

public List BestParent
{
        get { return bestparent; }
        set
        { 
            bestparent = value;
            OnPropertyChanged(nameof(BestParent));
        }
}

A la clase ChessPiece le he añadido una propiedad nueva llamada Picture para incluirle la forma de la pieza de ajedrez.
Acto seguido, preparamos nuesto código XAMl, donde yo lo que he hecho, como nuestra clase ChessBoard era una lista de objetos ChessPiece, no he cambiado a coordenadas y simplemente he incluido 64 Textblock, uno por casilla y el DataContext de cada Textblock es el que corresponde a cada objeto de la lista de objetos ChessPiece. Posteriormente cada Textblock tendrá un style asociado donde se le crea el Binding con el texto o la figura de ajedrez. (Las piezas de ajedrez son caracteres Unicode. ♜♞♝♛♚♟♖♘♗♕♔♙).
El estilo aplicado a cada Textblock es el siguiente:

<Style x:Key="piece" TargetType="TextBlock">
            <Setter Property="Text" Value="{Binding Picture}"/>

Código XAML

<Window x:Class="ChessBoardProject.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ChessBoardProject"
        mc:Ignorable="d"
        Title="MainWindow" Height="600" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="20"/>
            <ColumnDefinition Width="20*"/>
            <ColumnDefinition Width="20"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="20"/>
            <RowDefinition Height="20*"/>
            <RowDefinition Height="20"/>
        </Grid.RowDefinitions>
        <Grid Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" x:Name="board">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="55"/>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="55"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="55"/>
                <RowDefinition Height="50"/>
                <RowDefinition Height="50"/>
                <RowDefinition Height="50"/>
                <RowDefinition Height="50"/>
                <RowDefinition Height="50"/>
                <RowDefinition Height="50"/>
                <RowDefinition Height="55"/>
            </Grid.RowDefinitions>
            

            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="0" Grid.Column="0"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="0" Grid.Column="1"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="0" Grid.Column="2"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="0" Grid.Column="3"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="0" Grid.Column="4"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="0" Grid.Column="5"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="0" Grid.Column="6"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="0" Grid.Column="7"/>

            <Rectangle Style="{StaticResource blackCell}" Grid.Row="1" Grid.Column="0"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="1" Grid.Column="1"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="1" Grid.Column="2"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="1" Grid.Column="3"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="1" Grid.Column="4"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="1" Grid.Column="5"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="1" Grid.Column="6"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="1" Grid.Column="7"/>

            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="2" Grid.Column="0"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="2" Grid.Column="1"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="2" Grid.Column="2"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="2" Grid.Column="3"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="2" Grid.Column="4"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="2" Grid.Column="5"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="2" Grid.Column="6"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="2" Grid.Column="7"/>

            <Rectangle Style="{StaticResource blackCell}" Grid.Row="3" Grid.Column="0"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="3" Grid.Column="1"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="3" Grid.Column="2"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="3" Grid.Column="3"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="3" Grid.Column="4"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="3" Grid.Column="5"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="3" Grid.Column="6"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="3" Grid.Column="7"/>

            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="4" Grid.Column="0"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="4" Grid.Column="1"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="4" Grid.Column="2"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="4" Grid.Column="3"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="4" Grid.Column="4"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="4" Grid.Column="5"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="4" Grid.Column="6"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="4" Grid.Column="7"/>

            <Rectangle Style="{StaticResource blackCell}" Grid.Row="5" Grid.Column="0"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="5" Grid.Column="1"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="5" Grid.Column="2"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="5" Grid.Column="3"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="5" Grid.Column="4"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="5" Grid.Column="5"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="5" Grid.Column="6"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="5" Grid.Column="7"/>

            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="6" Grid.Column="0"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="6" Grid.Column="1"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="6" Grid.Column="2"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="6" Grid.Column="3"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="6" Grid.Column="4"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="6" Grid.Column="5"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="6" Grid.Column="6"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="6" Grid.Column="7"/>

            <Rectangle Style="{StaticResource blackCell}" Grid.Row="7" Grid.Column="0"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="7" Grid.Column="1"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="7" Grid.Column="2"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="7" Grid.Column="3"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="7" Grid.Column="4"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="7" Grid.Column="5"/>
            <Rectangle Style="{StaticResource blackCell}" Grid.Row="7" Grid.Column="6"/>
            <Rectangle Style="{StaticResource whiteCell}" Grid.Row="7" Grid.Column="7"/>

            <Rectangle Fill="Transparent" Stroke="Black" StrokeThickness="3" Grid.RowSpan="8" Grid.ColumnSpan="8"/>

            <TextBlock DataContext="{Binding BestParent[0]}" Style="{StaticResource piece}"  Grid.Row="0" Grid.Column="0"/>
            <TextBlock DataContext="{Binding BestParent[1]}" Style="{StaticResource piece}"  Grid.Row="0" Grid.Column="1"/>
            <TextBlock DataContext="{Binding BestParent[2]}" Style="{StaticResource piece}"  Grid.Row="0" Grid.Column="2"/>
            <TextBlock DataContext="{Binding BestParent[3]}" Style="{StaticResource piece}"  Grid.Row="0" Grid.Column="3"/>
            <TextBlock DataContext="{Binding BestParent[4]}" Style="{StaticResource piece}"  Grid.Row="0" Grid.Column="4"/>
            <TextBlock DataContext="{Binding BestParent[5]}" Style="{StaticResource piece}"  Grid.Row="0" Grid.Column="5"/>
            <TextBlock DataContext="{Binding BestParent[6]}" Style="{StaticResource piece}"  Grid.Row="0" Grid.Column="6"/>
            <TextBlock DataContext="{Binding BestParent[7]}" Style="{StaticResource piece}"  Grid.Row="0" Grid.Column="7"/>

            <TextBlock DataContext="{Binding BestParent[8]}" Style="{StaticResource piece}"   Grid.Row="1" Grid.Column="0"/>
            <TextBlock DataContext="{Binding BestParent[9]}" Style="{StaticResource piece}"   Grid.Row="1" Grid.Column="1"/>
            <TextBlock DataContext="{Binding BestParent[10]}" Style="{StaticResource piece}"  Grid.Row="1" Grid.Column="2"/>
            <TextBlock DataContext="{Binding BestParent[11]}" Style="{StaticResource piece}"  Grid.Row="1" Grid.Column="3"/>
            <TextBlock DataContext="{Binding BestParent[12]}" Style="{StaticResource piece}"  Grid.Row="1" Grid.Column="4"/>
            <TextBlock DataContext="{Binding BestParent[13]}" Style="{StaticResource piece}"  Grid.Row="1" Grid.Column="5"/>
            <TextBlock DataContext="{Binding BestParent[14]}" Style="{StaticResource piece}"  Grid.Row="1" Grid.Column="6"/>
            <TextBlock DataContext="{Binding BestParent[15]}" Style="{StaticResource piece}"  Grid.Row="1" Grid.Column="7"/>

            <TextBlock DataContext="{Binding BestParent[16]}" Style="{StaticResource piece}"  Grid.Row="2" Grid.Column="0"/>
            <TextBlock DataContext="{Binding BestParent[17]}" Style="{StaticResource piece}"  Grid.Row="2" Grid.Column="1"/>
            <TextBlock DataContext="{Binding BestParent[18]}" Style="{StaticResource piece}"  Grid.Row="2" Grid.Column="2"/>
            <TextBlock DataContext="{Binding BestParent[19]}" Style="{StaticResource piece}"  Grid.Row="2" Grid.Column="3"/>
            <TextBlock DataContext="{Binding BestParent[20]}" Style="{StaticResource piece}"  Grid.Row="2" Grid.Column="4"/>
            <TextBlock DataContext="{Binding BestParent[21]}" Style="{StaticResource piece}"  Grid.Row="2" Grid.Column="5"/>
            <TextBlock DataContext="{Binding BestParent[22]}" Style="{StaticResource piece}"  Grid.Row="2" Grid.Column="6"/>
            <TextBlock DataContext="{Binding BestParent[23]}" Style="{StaticResource piece}"  Grid.Row="2" Grid.Column="7"/>

            <TextBlock DataContext="{Binding BestParent[24]}" Style="{StaticResource piece}"  Grid.Row="3" Grid.Column="0"/>
            <TextBlock DataContext="{Binding BestParent[25]}" Style="{StaticResource piece}"  Grid.Row="3" Grid.Column="1"/>
            <TextBlock DataContext="{Binding BestParent[26]}" Style="{StaticResource piece}"  Grid.Row="3" Grid.Column="2"/>
            <TextBlock DataContext="{Binding BestParent[27]}" Style="{StaticResource piece}"  Grid.Row="3" Grid.Column="3"/>
            <TextBlock DataContext="{Binding BestParent[28]}" Style="{StaticResource piece}"  Grid.Row="3" Grid.Column="4"/>
            <TextBlock DataContext="{Binding BestParent[29]}" Style="{StaticResource piece}"  Grid.Row="3" Grid.Column="5"/>
            <TextBlock DataContext="{Binding BestParent[30]}" Style="{StaticResource piece}"  Grid.Row="3" Grid.Column="6"/>
            <TextBlock DataContext="{Binding BestParent[31]}" Style="{StaticResource piece}"  Grid.Row="3" Grid.Column="7"/>

            <TextBlock DataContext="{Binding BestParent[32]}" Style="{StaticResource piece}"  Grid.Row="4" Grid.Column="0"/>
            <TextBlock DataContext="{Binding BestParent[33]}" Style="{StaticResource piece}"  Grid.Row="4" Grid.Column="1"/>
            <TextBlock DataContext="{Binding BestParent[34]}" Style="{StaticResource piece}"  Grid.Row="4" Grid.Column="2"/>
            <TextBlock DataContext="{Binding BestParent[35]}" Style="{StaticResource piece}"  Grid.Row="4" Grid.Column="3"/>
            <TextBlock DataContext="{Binding BestParent[36]}" Style="{StaticResource piece}"  Grid.Row="4" Grid.Column="4"/>
            <TextBlock DataContext="{Binding BestParent[37]}" Style="{StaticResource piece}"  Grid.Row="4" Grid.Column="5"/>
            <TextBlock DataContext="{Binding BestParent[38]}" Style="{StaticResource piece}"  Grid.Row="4" Grid.Column="6"/>
            <TextBlock DataContext="{Binding BestParent[39]}" Style="{StaticResource piece}"  Grid.Row="4" Grid.Column="7"/>

            <TextBlock DataContext="{Binding BestParent[40]}" Style="{StaticResource piece}"  Grid.Row="5" Grid.Column="0"/>
            <TextBlock DataContext="{Binding BestParent[41]}" Style="{StaticResource piece}"  Grid.Row="5" Grid.Column="1"/>
            <TextBlock DataContext="{Binding BestParent[42]}" Style="{StaticResource piece}"  Grid.Row="5" Grid.Column="2"/>
            <TextBlock DataContext="{Binding BestParent[43]}" Style="{StaticResource piece}"  Grid.Row="5" Grid.Column="3"/>
            <TextBlock DataContext="{Binding BestParent[44]}" Style="{StaticResource piece}"  Grid.Row="5" Grid.Column="4"/>
            <TextBlock DataContext="{Binding BestParent[45]}" Style="{StaticResource piece}"  Grid.Row="5" Grid.Column="5"/>
            <TextBlock DataContext="{Binding BestParent[46]}" Style="{StaticResource piece}"  Grid.Row="5" Grid.Column="6"/>
            <TextBlock DataContext="{Binding BestParent[47]}" Style="{StaticResource piece}"  Grid.Row="5" Grid.Column="7"/>

            <TextBlock DataContext="{Binding BestParent[48]}" Style="{StaticResource piece}"  Grid.Row="6" Grid.Column="0"/>
            <TextBlock DataContext="{Binding BestParent[49]}" Style="{StaticResource piece}"  Grid.Row="6" Grid.Column="1"/>
            <TextBlock DataContext="{Binding BestParent[50]}" Style="{StaticResource piece}"  Grid.Row="6" Grid.Column="2"/>
            <TextBlock DataContext="{Binding BestParent[51]}" Style="{StaticResource piece}"  Grid.Row="6" Grid.Column="3"/>
            <TextBlock DataContext="{Binding BestParent[52]}" Style="{StaticResource piece}"  Grid.Row="6" Grid.Column="4"/>
            <TextBlock DataContext="{Binding BestParent[53]}" Style="{StaticResource piece}"  Grid.Row="6" Grid.Column="5"/>
            <TextBlock DataContext="{Binding BestParent[54]}" Style="{StaticResource piece}"  Grid.Row="6" Grid.Column="6"/>
            <TextBlock DataContext="{Binding BestParent[55]}" Style="{StaticResource piece}"  Grid.Row="6" Grid.Column="7"/>

            <TextBlock DataContext="{Binding BestParent[56]}" Style="{StaticResource piece}"  Grid.Row="7" Grid.Column="0"/>
            <TextBlock DataContext="{Binding BestParent[57]}" Style="{StaticResource piece}"  Grid.Row="7" Grid.Column="1"/>
            <TextBlock DataContext="{Binding BestParent[58]}" Style="{StaticResource piece}"  Grid.Row="7" Grid.Column="2"/>
            <TextBlock DataContext="{Binding BestParent[59]}" Style="{StaticResource piece}"  Grid.Row="7" Grid.Column="3"/>
            <TextBlock DataContext="{Binding BestParent[60]}" Style="{StaticResource piece}"  Grid.Row="7" Grid.Column="4"/>
            <TextBlock DataContext="{Binding BestParent[61]}" Style="{StaticResource piece}"  Grid.Row="7" Grid.Column="5"/>
            <TextBlock DataContext="{Binding BestParent[62]}" Style="{StaticResource piece}"  Grid.Row="7" Grid.Column="6"/>
            <TextBlock DataContext="{Binding BestParent[63]}" Style="{StaticResource piece}"  Grid.Row="7" Grid.Column="7"/>
        </Grid>
        <Button Content="Start" Grid.Column="1" HorizontalAlignment="Right" Grid.Row="1" VerticalAlignment="Bottom" Width="75" Margin="20" Click="Button_Click"/>
    </Grid>
   
</Window>

Para no bloquear la interfaz, creamos una tarea y ejecutamos. Le he puesto un retardo de unos cuantos milisegundos en cada solución parcial para ver como actua el algoritmo y el resultado.
ScreenCapture_05-09-2020 12.44.10.gif
Licencia Creative Commons
Esta obra está bajo una Licencia Creative Commons Atribución-CompartirIgual 4.0 Internacional.

Contraseñas y entropía

Contraseñas y entropía

Hoy un amigo nos ha enviado un mensaje al grupo de la Promoción para ver que opinábamos al respecto de la siguiente imagen

IMG-20200902-WA0018.jpg

y yo que la he visto, instantáneamente me he acordado del cálculo necesario para rellenar esta tabla.

Lo primero de todo se debe calcular la entropía. La entropía de una contraseña es calculo matemático que representa un indicador de como se encuentra esta contraseña sobre una población, si incluimos en nuestra contraseña mayúsculas, minúsculas, números y símbolos, la entropía de nuestra contraseña será mayor puesto que la población de caracteres usados es mucho mayor, en este caso, unos 94 (26 minúsculas, 26 mayúsculas, 10 números y 32 símbolos). La fórmula es la siguiente:

entropia.png

o lo que es lo mismo, es el logaritmo en base de 2 de la cantidad de elementos de la población por la longitud de la cadena. Si efectuamos el cálculo inverso, 2 elevado a la entropía debe ser igual a la población elevada al número de caracteres de  nuestra contraseña, que esto serían el número máximo de operaciones necesarias para intentar reventar nuestra password por fuerza bruta.

Si nuestra contraseña usa solo las minúsculas, su «pool» sería de 26 y por tanto la contraseña tendría una entropía baja y fácil de descubrir.

Si hay alguien que quiera comprobar la fuerza de su contraseña online puede hacerlo desde Kaspersky
Bien, pero lo que a mi me importa es como sabemos, con código. En la clase le he incluido una penalización si nuestra contraseña se encuentra entre las más conocidas.

En C#

    /*
************************************************************************************************************************************************
    * © JOAQUIN MARTINEZ RUS 2020
    * Archivo:         PasswordEntropy.cs
    * Descripción:     Clase que calcula la entropia y el tiempo en descifrar por fuerza bruta de de una contraseña
    * Historial:
    *                  1. Joaquin Martínez Rus - 02 sep 2020. Creación
    *
    * Comentarios:
    *
    *
    ****************************************************************/
    public class PasswordEntropy
    {
        public PasswordEntropy(string password)
        {
            Password = password;

        }

        public string Password { get; set; }
        public double Entropy => GetEntropy();
        public string EntropyToString => GetTimeByEntropy(Entropy);

        private double GetEntropy()
        {
            //Pools
            double pool = 0;

            // Numbers
            pool += Password.Any(k => char.IsDigit(k)) ? 10 : 0;
            // Lower 26
            pool += Password.Any(k => char.IsLower(k)) ? 26 : 0;
            // Upper 26
            pool += Password.Any(k => char.IsUpper(k)) ? 26 : 0;
            // Simbols 30
            pool += Password.Any(k => (char.IsSymbol(k) || char.IsPunctuation(k))) ? 32 : 0;

            if (pool == 0) return pool;
            // entropy = Lenght * log pool / log2
            var p = Math.Log(pool, 2);
            var entropy = Password.Length * Math.Log(pool, 2);

            // el uso de una contraseña famosa, indica una falta de seguridad absoluta
            // por lo que no se incluye como deducción de entropía, sino que se incluye en el
            // mismo cálculo de la entropía
            if (IsFamous(Password))
            {
                entropy = (int)Entropies.NoSecurity - 1; // Inexistencia de seguridad
            }
            return entropy;
        }

        private const double OPERATIONPERSECOND = 4*10E12;

        private bool IsFamous(string password)
        {
            var list = new List()
            {
                "1234",
                "2000",
                "12345",
                "111111",
                "123123",
                "123456",
                "654321",
                "696969",
                "1234567",
                "12345678",
                "abc123",
                "alejandra",
                "america",
                "baseball",
                "bonita",
                "daniel",
                "dragon",
                "estrella",
                "football",
                "harley",
                "iloveyou",
                "jennifer",
                "jesus",
                "jordan",
                "letmein",
                "mariposa",
                "master",
                "michael",
                "monkey",
                "mustang",
                "password",
                "pussy",
                "qwerty",
                "roberto",
                "sebastian",
                "shadow",
                "superman",
                "Tequiero"
            };
            return list.Contains(password);
        }

        public enum Entropies
        {
            Blank = 0,
            NoSecurity =10,
            TooWeak=20,
            VeryWeak=28,
            Weak=35,
            Medium=55,
            Strong=75,
            Stronger=127,
            OverTheTopStrong = 190
        }

        private string GetTimeByEntropy(double entropy)
        {
            if (entropy == 0) return string.Empty;
            string output = "";

            double value = Math.Pow(2, entropy) / OPERATIONPERSECOND;
            double ms = 0;
            double seconds = value;
            double minutes = seconds / 60;
            double hours = minutes / 60;
            double days = hours / 24;
            double months = days / 30;
            double years = months / 12;
            double centuries = years / 100;
            double millennium = centuries / 10;

            seconds = value;
            if (value == 0) return "";

            if (seconds > 0 && seconds  59 && hours > 24 && days  11 && years < 100 && centuries < 1000000)
            {
                output = "Tu contraseña puede ser descifrada en más de un millón de años!";
            }
            else
            {
                millennium = Math.Round(millennium, 1);
                output = $"Tu contraseña puede ser descifrada en {millennium}, {(millennium == 1 ? "milenio" : "milenios")}";
            }

            return output;
        }
    }

static void Main(string[] args)
        {
            var password = "x#Pr0m0cio_N";

            for (int i = 1; i <= password.Length; i++)
            {
                var pwd = new PasswordEntropy(password.Substring(0,i));
                Console.WriteLine($"Password: {pwd.Password}\n{pwd.EntropyToString}");
                Console.WriteLine("Press any key");
            }

            Console.ReadKey();
        }

el resultado iterando por la contraseña que he creado


Password: x
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#P
Tu contraseña puede ser descifrada en 0, milisegundos
Press any key
Password: x#Pr
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#Pr0
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#Pr0m
Tu contraseña puede ser descifrada en 17 milisegundos
Press any key
Password: x#Pr0m0
Tu contraseña puede ser descifrada en 2 segundos
Press any key
Password: x#Pr0m0c
Tu contraseña puede ser descifrada en 3 minutos
Press any key
Password: x#Pr0m0ci
Tu contraseña puede ser descifrada en 4 horas
Press any key
Password: x#Pr0m0cio
Tu contraseña puede ser descifrada en 15,6 dias
Press any key
Password: x#Pr0m0cio_
Tu contraseña puede ser descifrada en 4,1 años
Press any key
Password: x#Pr0m0cio_N
Tu contraseña puede ser descifrada en 3,8 siglos
Press any key

y en Kotlin

class PasswordEntropy(val password: String) {

    init {

    }

    fun entropy(): Double {

        //Pools
        var pool = 0.0

        // Numbers
        pool += if (password.any { c -> c.isDigit() }) 10.0 else 0.0
        // Lower 26
        pool += if (password.any { c -> c.isLowerCase() }) 26.0 else 0.0
        // Upper 26
        pool += if (password.any { c -> c.isUpperCase() }) 26.0 else 0.0
        val (noSymbols, symbols) = password.partition { it.isLetter() || it.isDigit() }
        // Simbols 32
        pool += if (symbols.count()>0) 32.0 else 0.0
        if (pool == 0.0) return pool
        // entropy = Lenght * log pool / log2

        var entropy = password.length * log2(pool)

        // el uso de una contraseña famosa, indica una falta de seguridad absoluta
        // por lo que no se incluye como deducción de entropía, sino que se incluye en el
        // mismo cálculo de la entropía
        if (isFamous()) {
            entropy = 9.0 // Inexistencia de seguridad
        }
        return entropy
    }

    private fun isFamous(): Boolean{
        return listOf(
            "1234",
            "2000",
            "12345",
            "111111",
            "123123",
            "123456",
            "654321",
            "696969",
            "1234567",
            "12345678",
            "abc123",
            "alejandra",
            "america",
            "baseball",
            "bonita",
            "daniel",
            "dragon",
            "estrella",
            "football",
            "harley",
            "iloveyou",
            "jennifer",
            "jesus",
            "jordan",
            "letmein",
            "mariposa",
            "master",
            "michael",
            "monkey",
            "mustang",
            "password",
            "pussy",
            "qwerty",
            "roberto",
            "sebastian",
            "shadow",
            "superman",
            "Tequiero").contains(password)

    }
    private val OPERATION_PER_SECOND: Double = 4*10E12

    fun entropyToString(): String {
        val entropy = entropy()
        if (entropy == 0.0) return ""

        val value = 2.0.pow(entropy) / OPERATION_PER_SECOND
        var ms = 0
        var seconds = value
        val minutes = seconds / 60.0
        val hours = minutes / 60.0
        val days = hours / 24.0
        val months = days / 30.0
        val years = months / 12.0
        val centuries = years / 100.0
        val millennium = centuries / 10.0

        seconds = value
        if (value == 0.0) return ""

        return "Contraseña: $password\n${if (ifa) "Es una contraseña conocida!\n" else ""}${when {
                    ms in 1..999 ->"Tu contraseña puede ser descifrada inmediatamente, yo no lo llamaría contraseña"
                    seconds > 0 && seconds  "Tu contraseña puede ser descifrada en ${seconds.toInt()} ${if (seconds.toInt() == 1) "segundo" else "segundos"}"
                    seconds > 59 && minutes   "Tu contraseña puede ser descifrada en ${minutes.toInt()} ${if (minutes.toInt() == 1) "minuto" else "minutos"}"
                    minutes > 59 && hours "Tu contraseña puede ser descifrada en ${hours.toInt()} ${if (hours.toInt() == 1) "hora" else "horas"}"
                    hours > 24 && days  "Tu contraseña puede ser descifrada en ${days.toInt()} ${if (days.toInt() == 1) "dia" else "dias"}"
                    days > 30 && months  "Tu contraseña puede ser descifrada en ${months.toInt()} ${if (months.toInt() == 1) "mes" else "meses"}"
                    months > 11 && years   "Tu contraseña puede ser descifrada en ${years.toInt()} ${if (years.toInt() == 1) "año" else "años"}"
                    years > 99 && centuries  "Tu contraseña puede ser descifrada en ${"%.1f".format(centuries)} ${if (centuries == 1.0) "siglo" else "siglos"}"
                    millennium > 1000000 -> "Tu contraseña puede ser descifrada en más de un millón de años!"
            else -> "Tu contraseña puede ser descifrada en ${millennium.toInt()} ${if (millennium.toInt() == 1) "milenio" else "milenios"}"}}"
}

fun main(){
    val password = "x#Pr0m0cio_N";

    for (i in (1..password.length)) {
        val pwd = PasswordEntropy(password.substring(0,i))
        println("Password: ${pwd.password}\n${pwd.entropyToString()}\n")

    }
}

el resultado para Kotlin


Password: x
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#P
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#Pr
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#Pr0
Tu contraseña puede ser descifrada en 0 milisegundos
Press any key
Password: x#Pr0m
Tu contraseña puede ser descifrada en 17 milisegundos
Press any key
Password: x#Pr0m0
Tu contraseña puede ser descifrada en 2 segundos
Press any key
Password: x#Pr0m0c
Tu contraseña puede ser descifrada en 3 minutos
Press any key
Password: x#Pr0m0ci
Tu contraseña puede ser descifrada en 4 horas
Press any key
Password: x#Pr0m0cio
Tu contraseña puede ser descifrada en 15,6 dias
Press any key
Password: x#Pr0m0cio_
Tu contraseña puede ser descifrada en 4,1 años
Press any key
Password: x#Pr0m0cio_N
Tu contraseña puede ser descifrada en 3,8 siglos
Press any key

El archivo jar podéis descargarlo desde aquí

Archivo ZIP

SHA256 9314879A535675E813EAC13CEC9B056CAADC4C080E5E033E239E2D88A60F55E3

Archivo jar

SHA256 CA942FB971E34CB775E14F12C5B25E5E456044F02DD54447F5690FA05469C3C8

Saludos a la X Promoción del IPE 2!!!

 

Interfaces

Interfaces

Cuando se empieza a programar, el uso de interfaces es un poco abstracto ya que no se le ve utilidad, pero las interfaces son de los más útiles. ¿Qué es una interface? es un contrato por el que una clase se ve obligada a cumplir en todos sus términos. Es decir, la interface contiene métodos y propiedades y cuando una clase implementa esta interface, la clase debe tener absolutamente todos los métodos y todas las propiedades que la interface le ha dicho.

Un objeto en la mayoría de los lenguajes, solo puede heredar de una clase, pero si puede implementar varias interfaces.

Un ejemplo. Quiero crear una colección de figuras de ajedrez; si quisiera ordenar la colección por valor y color en el que las negras valiesen más, al usar el método sort(), orderby() o el que sea, el objeto como mínimo deberá contener algún método que use ese método para ordenar sus elementos, compararlos y decidir cual va primero y cual después, ¿no?, pues yo lo que haría sería por ejemplo en Kotlin, es que la clase FiguraAjedrez implemente la interface Comparable (lo normal es que las interfaces, dependen del lenguaje comiencen por I, pero todas acaben con -able) que a su vez contiene un método compareTo.

Pues si la FiguraAjedrez tiene ese método, la colección cuando use el método sort encontrará el método compareTo donde nosotros escribiremos nuestro código, comparamos por el valor de la figura y le añadimos un extra por el color.

También, cuando pasamos como argumento una interface, nos estamos asegurando que el objeto que se pasa cumple con las expectativas. En C# si pasamos como argumento en un método un objeto del tipo IEnumerable, no estamos asegurando que el objeto que se pasa, tiene como mínimo un enumerador y que pude moverse por los elementos de la colección. O si le pasamos como argumento la interface IList, nos estamos asegurando que esta interface está heredando de ICollection, IEnumerable<T> e IEnumerable, de modo que puede moverse por sus elementos, puede borrar la colección, añadir, eliminar objetos, saber si tiene un objeto en la colección y hacer copias, todo esto. En Kotlin pasa algo parecido con la interface MutableList que hereda de las interfaces List y MutableCollection para hacer estas mismas acciones y algunas más.

        private static void Foo(IList collection)
        {
            // Do something
        }
         fun  foo(collection: MutableList){
            // Do something
         }

A continuación vamos ver un ejemplo de interface y de una clase que la implemente

interface IChessPiece: Comparable{
    fun doSomething()
    fun move()
    val isBlack: Boolean
    val value: Int
    val piece: String
}
class ChessPiece(override val piece: String, override val isBlack: Boolean, override val value: Int) : IChessPiece {
    override fun doSomething() {
        TODO("Not yet implemented")
    }

    override fun move() {
        TODO("Not yet implemented")
    }

    override fun compareTo(other: ChessPiece): Int {
        // Aqui incluimos el código de comparación
    }

}

fun foo(chessPiece: IChessPiece){

}

En el ejemplo, vemos que la interface IChessPiece implementa IComparable, así que podremos comparar objetos con otros del mismo tipo y luego le añadimos algunas propiedades y métodos, así que la clase ChessPiece obligatoriamente, debe tener cada uno de los métodos y propiedades establecidos en la interface.
En el método foo pasamos como argumento un objeto que implemente la interface, ASEGURANDO que va a hacer y va a tener lo que queremos que haga y que tenga. si mañana cambiamos el código de la clase o creamos una nueva con más funciones, lo que nos importa para que se entienda con el resto del código es que cumpla como mínimo con lo esperado.Usa las interfaces, sobre todo en los modelos y viewmodel, más tarde te alegraras enormemente.
Licencia Creative Commons
Esta obra está bajo una Licencia Creative Commons Atribución-CompartirIgual 4.0 Internacional.

Sistema D’Hont con C#

Sistema D’Hont con C#

Dead XOR Alive

Dead XOR Alive

En uno de mis blogs, en Arithmos, publiqué un post sobre una historia de carceleros y presos; minutos después de acabar el artículo, dije, ¿por qué no hago una app que lo muestre?, dicho y hecho, me puse y en una tarde desarrollada estaba.

Y ¿por qué digo esto? para demostrar lo fácil que es desarrollar una pequeña aplicación con C# y WPF.

Si leen el artículo, podrán observar que la aplicación solo muestra un tablero de ajedrez, con monedas en cada casilla con una cara o una cruz aleatoriamente, un cuadro mágico que es el elegido por el carcelero y un cuadro del tablero que debe calcular uno de los presos para que el siguiente convicto les salve de la muerte, este último cuadro no es otro que una operación XOR.

El modelo podría ser una clase Chessboard que alberga objetos del tipo CoinBox, las monedas que ya de camino le diremos que color tiene el cuadro donde se apoyan en el tablero.

    public class Chessboard
    {
        public List CoinsBox { get; set; }

        public Chessboard() : this(-1) { }

        public Chessboard(int headsCount)
        {
            CoinsBox = new List();
            FillChessboard(headsCount);
        }

        ///
        /// Rellena el tablero con cuadros y monedas aleaotorias con cara y cruz
        /// 

        /// Número de caras. si es -1, se genera aletoriamente
        void FillChessboard(int headsCount)
        {
            var random = new Random();

            headsCount = headsCount == -1 ? random.Next(64) : headsCount;

            // Llenar Lista con monedas con cruz
            Enumerable.Range(0, 64).ToList()
            .ForEach(n => CoinsBox.Add(new CoinBox(n, false, (n / 8) % 2 == 0 ? n % 2 == 0 : n % 2 != 0)));

            // Cambiar n monedas a cara
            Enumerable.Range(0, headsCount).ToList().ForEach(n => CoinsBox[random.Next(64)].IsHeads = true);

            // Cuadro mágico
            CoinsBox[random.Next(64)].IsMagicBox = true;

            // Cuadro que debe cambiar
            var changed= CoinsBox
                .Where(n => n.IsHeads).Aggregate(MagicBox.Index, (acum, n) => acum ^ n.Index, op => op);
            CoinsBox[changed].IsChangedBox = true;

        }

        public CoinBox MagicBox => CoinsBox.Where(n => n.IsMagicBox).FirstOrDefault();
        public CoinBox ChangedBox => CoinsBox.Where(n => n.IsChangedBox).FirstOrDefault();
        public int ParityChessboard => CoinsBox
                .Where(n => n.IsHeads).Aggregate(0, (acum, n) => acum ^ n.Index, op => op);

        public string ParityChessboardToString => $"La paridad del tablero es {ParityChessboard}";
        public string ResultToString => $"Si cambiamos la moneda {ChangedBox.Index}, la paridad del tablero será {MagicBox.Index} que es el cuadrado mágico!!!";
        public string MagicBoxToString => $"El cuadro mágico seleccionado por el carcelero es el número {MagicBox.Index}";
        public string ChangeBoxToString => $"La moneda que debe cambiar el convicto es la número {ChangedBox.Index}";
        public string ListaXORToString => $"La operación XOR de las monedas que tienen cara {string.Join(", ", CoinsBox.Where(k => k.IsHeads).Select(i => i.Index))} y el cuadro mágico {MagicBox.Index} da como resultado {ChangedBox.Index} o la operación XOR entre el cuadro mágico {MagicBox.Index} y la paridad del tablero {ParityChessboard} es {ChangedBox.Index}";
    }

    public class CoinBox
    {

        public CoinBox(int index, bool isHeads, bool isBlack)
        {
            Index = index;
            IsHeads = isHeads;
            IsBlack = isBlack;
        }

        public int Index { get; set; }

        public bool IsHeads { get; set; }

        public bool IsTails { get { return !IsHeads; } }

        public bool IsChangedBox { get; set; }

        public bool IsMagicBox { get; set; }

        public bool IsBlack { get; set; }
    }

Como algo particular, la asignación del color del cuadro mediante la operación (n / 8) % 2 == 0 ? n % 2 == 0 : n % 2 != 0) con la que con el índice del cuadro le decimos si el cuadro es negro o blanco o las expresiones lambda que calculan la operación XOR sobre una lista de elementos mediante la función lambda Aggregate, la cual le pasamos un parámetro acumulador inicial y posteriormente es usado elemento a elemento con la operación XOR.Para la vista, he creado un control definido por el usuario llamado Box, el cual contiene el Binding cambiando su aspecto según las propiedades del objeto CoinBox asociado a su Datacontext. Como podéis ver a continuación, creo converter para transformar valores booleanos en Visibility, Brush o Bitmap. (Si alguien los quiere los converter, los cuelgo)

<UserControl x:Class="DxorAlive.Box"
             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:DxorAlive"
             mc:Ignorable="d" 
             d:DesignHeight="43" d:DesignWidth="43" Margin="1">
    <UserControl.Resources>
        <local:BoolToVisibleConverter x:Key="b2tvc" Reverse="False"/>
        <local:ImageConverter x:Key="itc"/>
        <local:BoolToColorConverter x:Key="btcc"/>
    </UserControl.Resources>
    <Grid>

        <Rectangle Width="64" Height="64" Stroke="Gray" StrokeThickness="1" Fill="{Binding IsBlack, Converter={StaticResource btcc}}"/>
        <Rectangle Width="62" Height="62" Stroke="Red" StrokeThickness="2" Visibility="{Binding IsMagicBox, Converter={StaticResource b2tvc}}"/>
        <Rectangle Width="60" Height="60" Stroke="Orange" StrokeThickness="2" Visibility="{Binding IsChangedBox, Converter={StaticResource b2tvc}}"/>
        <Image Source="{Binding IsHeads, Converter={StaticResource itc}}" Height="48"/>
    </Grid>
</UserControl>

Ahora solo nos queda crear o con xml o desde la clase de la vista (runtime) una matriz de objetos cada uno con su datacontext. Yo lo he he desarrollado con código en vez de crear 64 cuadros del tipo Box. Llamamos al método FillChessboard y se genera un nuevo tablero.

        private void FillChessboard(int headsCount)
        {
            grid.Children.Clear();
            // Crear tablero
            var cb = new Chessboard(headsCount);

            //crear cuadros y monedas
            cb.CoinsBox.ForEach(n =>
            {
                var c = new Box();
                c.DataContext = n;
                var row = n.Index / 8;
                var col = n.Index % 8;
                Grid.SetRow(c, row);
                Grid.SetColumn(c, col+1);
                grid.Children.Add(c);
            });

            this.DataContext = cb;

        }

he aquí el resultado. Un saludo a tod@s

deadxoralive1.png
Os dejo el enlace del ejecutable

PD: Y como llevo una temporada enamorado de Kotlin, os paso código en este lenguaje para una app de consola. I ♥ kt

 
import kotlin.random.Random

fun main(args: Array) {
    val c= Chessboard(5)
    println(c)

}


class Chessboard(var headsCount: Int) {
    val coinsBox = mutableListOf()

    init {
        headsCount = if (headsCount == -1) Random.nextInt(64) else headsCount
        fillChessboard()
    }

    private fun fillChessboard() {
        // Rellenar de monedas
        (0..63).toList().forEach {
            coinsBox.add(CoinBox(it, false, if ((it / 8) % 2 == 0) (it % 2 == 0) else (it % 2 != 0)))
        }
        // Caras
        (0..headsCount!!).toList().forEach { coinsBox[Random.nextInt(64)].isHeads = true }
        // cuadro mágico
        val vMB = Random.nextInt(64)
        coinsBox[vMB].isMagicBox = true
        // moneda cambiante
        coinsBox[coinsBox.filter { i-> i.isHeads }.fold(vMB) { acc, opXOR -> acc.xor(opXOR.index) }].isChangedBox=true

    }

    override fun toString(): String {
        return "$magic\n$parityToString\n$changed\n$result\n$listaXOR\n$chessboard"
    }

    val magicBox = coinsBox.filter { n -> n.isMagicBox }.firstOrNull()
    val changedBox = coinsBox.filter { n -> n.isChangedBox }.firstOrNull()
    val parityChessBoard = coinsBox[coinsBox.filter { i-> i.isHeads }.fold(0) { acc, opXOR -> acc.xor(opXOR.index) }]

    val parityToString= "La paridad del tablero es ${parityChessBoard.index}"
    val result = "Si cambiamos la moneda ${changedBox?.index}, la paridad del tablero será ${magicBox?.index} que es el cuadrado mágico"
    val magic = "El cuadro seleccionado por el carcelero es el número ${magicBox?.index}"
    val changed = "La moneda que debe cambiar el convicto es la número ${changedBox?.index}"
    val listaXOR = "La operación XOR de las monedas que tienen cara ${coinsBox.filter { k-> k.isHeads }.joinToString(", "){it.index.toString()}} " +
            "y el cuadro mágico ${magicBox?.index} da como resultado ${changedBox?.index} " +
            "\no la operación XOR entre el cuadro mágico ${magicBox?.index} y la paridad del tablero ${parityChessBoard.index} es ${changedBox?.index}"
    val chessboard = coinsBox.joinToString("") {k-> "\t${k.index} ${if (k.isHeads) "H" else "T"} ${if ((k.index+1) % 8==0) "\n" else ""}"  }

}


data class CoinBox(
    val index: Int,
    var isHeads: Boolean,
    var isBlack: Boolean,
    var isChangedBox: Boolean = false,
    var isMagicBox: Boolean = false

Binding WPF vs Binding Android – I

Binding WPF vs Binding Android – I
Este post, es el inicio de una tetralogía con el que pretendo mostrar los aspectos de desarrollo de ambas plataformas, Microsoft y Android en cuanto al uso de la arquitectura MVVM y Databinding.
Antes de nada veamos cuales son las diferencias esenciales del modelo vista controlador (MVC) y el modelo vista vista-modelo (MVVM).

Colecciones I

Colecciones I

Hoy vamos a ver como aprovecharnos de herramientas que nos permiten usar un código más limpio y entendible en cuanto a colecciones se refiere y para ello veremos como hacerlo en este post para colecciones con expresiones lambda y Linq de .NET con C# y en el siguiente post, haremos esto mismo pero con Java.

En primer lugar crearemos un clase  Persona, dándole algunas propiedades como nombre, apellidos, nacimiento, un valor booleano para conocer el género, fecha de nacimiento y lugar de nacimiento, además le añadimos una propiedad de solo lectura que calcule la edad, toString, una para obtener el nombre completo y otro método booleano para saber si es nacido en un lugar concreto.

namespace ColeccionesDemo
{
    public class Persona
    {
        public Persona(string _name,
            string _surname,
            bool _isMan,
            DateTime _birthDate,
            string _birthPlace)
        {
            Name = _name;
            SurName = _surname;
            IsMan = _isMan;
            BirthDate = _birthDate;
            BirthPlace = _birthPlace;
        }

        public string Name { get; set; }
        public string SurName { get; set; }
        public DateTime BirthDate { get; set; }
        public string BirthPlace { get; set; }
        public bool IsMan { get; set; }
        public int Age
        {
            get
            {
                DateTime now = DateTime.Now;
                return now.Year - BirthDate.Year + (now.DayOfYear Nombre: {0}, Apellidos: {1}, Edad: {2}, De: {3}", Name, SurName, Age, BirthPlace); }
        }
        public string FullName
        {
            get { return string.Format("{0} {1}", Name, SurName); }
        }
        public override bool Equals(object obj)
        {
            var persona = obj as Persona;
            return persona != null &&
                   Name == persona.Name &&
                   SurName == persona.SurName;
        }
    }
}

Una vez creada la clase persona, para C# vamos a crear un objeto que almacene esta clase con una colección List la cual implementa la interfaz IEnumerable y a la cual le añadiremos algunos individuos ficticios.

      static List personas = new List();

        static void addPersonas()
        {
            personas.Add(new Persona("Joaquin", "Martinez Rus", true, new DateTime(1969, 2, 20), "Linares"));
            personas.Add(new Persona("Vicky", "Diez Calatayud", false, new DateTime(1996, 4, 19), "Calatayud"));
            personas.Add(new Persona("Ana Isabel", "Martinez Baloo", false, new DateTime(1998, 10, 20), "Cordoba"));
            personas.Add(new Persona("Victoria", "Martinez Nurse", false, new DateTime(1992, 1, 21), "Cordoba"));
            personas.Add(new Persona("Ana", "Rus Maria", false, new DateTime(1961, 11, 3), "Linares"));
            personas.Add(new Persona("Manuel", "Bonillo Contador", true, new DateTime(1978, 12, 26), "Cordoba"));
            personas.Add(new Persona("Jose Antonio", "Martinez Arcos", true, new DateTime(1974, 9, 4), "Zaragoza"));
            personas.Add(new Persona("Vicente", "Rodriguez Iglesias", true, new DateTime(1981, 11, 8), "Cordoba"));
            personas.Add(new Persona("Alfonso", "Perez Judicial", true, new DateTime(2000, 5, 5), "Linares"));
            personas.Add(new Persona("Ana", "Martinez Maestre", false, new DateTime(2004, 3, 4), "Linares"));
            personas.Add(new Persona("Magdalena", "Fuentes Cruz", false, new DateTime(2002, 7, 11), "Jaen"));
            personas.Add(new Persona("Jose", "Martinez Cintas", true, new DateTime(1981, 6, 21), "Linares"));
            personas.Add(new Persona("Jose", "Aldea Morata", true, new DateTime(1994, 11, 22), "Calatayud"));
            personas.Add(new Persona("Maria Isabel", "Diez Campieles", false, new DateTime(2005, 8, 1), "Calatayud"));
            personas.Add(new Persona("Maria", "Bella Adriá", false, new DateTime(2001, 10, 22), "Jaen"));
            personas.Add(new Persona("Pedro José", "Garcia Civil", true, new DateTime(1980, 9, 15), "Cordoba"));
            personas.Add(new Persona("Luis", "Jiloca Diez", true, new DateTime(1995, 6, 15), "Zaragoza"));
            personas.Add(new Persona("Juan", "Pintor Escultor", true, new DateTime(2000, 10, 22), "Jaen"));
            personas.Add(new Persona("Juan", "Goran Esteban", true, new DateTime(1974, 7, 14), "Jaen"));
            personas.Add(new Persona("Andrea", "Jiloca Diez", false, new DateTime(1999, 9, 21), "Calatayud"));
            personas.Add(new Persona("Daniel", "Ceular Flower", true, new DateTime(1975, 2, 4), "Cordoba"));<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>
        }

Una vez añadidos, vamos a pedirle a nuestra colección de personas unas cuantos datos con criterios concretos.
Ejemplo 1. ¿cual es el promedio de edad de todas las personas nacidas en Calatayud?
Vamos a crear tres métodos, uno paso a paso, calcAverageAge(string _birthPlace), es decir inicializamos variables, iteramos por la colección, comprobamos la condición, calculamos e imprimimos. El segundo método mediante programación funcional calcAverageAgeFunctionalProgramming(string _birthPlace), escribiremos una línea para calcular el promedio y otra para imprimirlo (aunque lo podía haber hecho en la misma línea), por último otro modo sería con Linq igual de vistoso que el anterior con el método calcAverageAgeLinq(string _birthPlace)

static void calcAverageAge(string _birthPlace)
        {
            double suma = 0;
            double count = 0;
            double average = 0;
            foreach (var persona in personas)
            {
                if (persona.IsFrom(_birthPlace))
                {
                    suma += persona.Age;
                    count++;
                }
            }
            average = suma / count;
            Console.WriteLine(string.Format("Las personas nacidas en {0} tienen un promedio de edad de {1} ", _birthPlace, average));

        }

        static void calcAverageAgeFunctionalProgramming(string _birthPlace)
        {
            var average= personas.Where(n => n.IsFrom(_birthPlace)).Average(p => p.Age);
            Console.WriteLine(string.Format("Programación funcional\nLas personas nacidas en {0} tienen un promedio de edad de {1} ", _birthPlace, average));
        }

        static void calcAverageAgeLinq(string _birthPlace)
        {
            var average = (from u in personas
                         where u.IsFrom(_birthPlace)
                         select u).Average(p => p.Age);
            Console.WriteLine(string.Format("Programación funcional\nLas personas nacidas en {0} tienen un promedio de edad de {1} ", _birthPlace, average));
        }

Ejemplo 2. Queremos listar todos los mayores de edad por orden alfabético de los apellidos.
Con el método estándar printAdults() y como queremos ordenar por los apellidos hemos tenido que incluir la interfaz IComparable que implementa el método CompareTo(Persona other) efectuando la comparación de los apellidos y ejecutando el método Sort de la clase List. En el siguiente método con programación funcional printAdultsFunctionalProgramming(), en una sola línea efectuamos todas las operaciones sin hacer referencia a ninguna interfaz IComparable y por último el mismo método basado en Linq printAdultsLinq(). Este tipo de expresiones en una línea, las llamamos expresiones lambda.

static void printAdults()
        {
            personas.Sort();
            foreach (var persona in personas)
            {
                if (persona.Age>17)
                {
                    Console.WriteLine(persona.FullName);
                }
            }
        }

        static void printAdultsFunctionalProgramming()
        {
            Console.WriteLine("******************************************");
            Console.WriteLine("Mayores de edad con programación funcional");
            Console.WriteLine("******************************************");
            personas.Where(p => p.Age > 17).
                OrderBy(p=>p.SurName).
                ToList().
                ForEach(p => Console.WriteLine(p.FullName));
        }

        static void printAdultsLinq()
        {
            Console.WriteLine("************************");
            Console.WriteLine("Mayores de edad con Linq");
            Console.WriteLine("************************");
            (from persona in personas
                      where persona.Age > 17
                      orderby persona.SurName
                      select persona).ToList().ForEach(p => Console.WriteLine(p.FullName));
        }

Resultado, el mismo, pero a mi entender, claridad en el código máxima con programación funcional. En el siguiente post, haremos lo mismo pero con Java.
coleccion1coleccion2

Documentar el código

Documentar el código

El código además de ser limpio, legible, entendible o estructurado entre otras características, debe estar muy bien documentado con el fin de efectuar un mantenimiento, modificación o ampliación de nuestra fuente. Además, es posible generar documentación en base a estos comentarios como es el caso de JavaDoc.

Existen muchas formas de comentar el código, por ejemplo:

C#, Java, PHP, etc.

  • Solo C#. Podemos hacerlo con tres barras /// en la que al crear una nueva línea, la siguiente línea queda también comentada con ///. Si las escribimos antes de una clase o un método, genera etiquetas de comentario que expondré a continuación.
  • Dos barras, crea una línea de código comentado
  • Comentario multilínea. Una barra y un asterisco /* provoca que el código esté comentado hasta que se cierre el comentario con asterisco barra */

Visual basic.NET

  • Comillas simples para comentar línea. ‘Esto es un comentario en VB.NET
  • Usar la etiqueta REM. REM Esto es un comentario en VB.NET
  • Si queremos crear varias líneas, debemos usar el concatenador guión bajo
    REM Esto es un comentario_ 
    en VB.NET

HTML

  • para cerrar el comentario

Comentarios de clases y métodos

Cuando estamos escribiendo código e instanciamos una clase o seleccionamos un método de una clase, los IDE,s nos muestran una ayuda que nos muestra que hace el método, que parámetros tiene o que sobrecargas y esto se consigue del siguiente modo:

C#

    • Escribimos /// precediendo a una clase o un método y se generan automáticamente una serie de etiquetas xml. La documentación de las etiquetas la pueden encontrar en el siguiente enlace.
      /// <summary>
      /// Obtiene el nombre completo del objeto Persona
      /// </summary>
      /// <param name="dni">DNI del objeto persona</param>
      /// <returns>Cadena de texto con el nombre completo</returns>
      public string GetFullName(string dni)
      {
      	return "Joaquín Martínez;
      }
      
      /// <summary>
      /// Obtiene el nombre completo del objeto Persona
      /// </summary>
      /// <param name="id">Identificador del objeto persona</param>
      /// <returns>Cadena de texto con el nombre completo</returns>
      public string GetFullName(int id)
      {
      	return "Joaquín Martínez;
      }
      

Java

    • Escribimos encima de un método /** e INTRO y se genera las etiquetas para documentar el código
      /**
      * Obtiene el nombre completo del objeto Persona
      * @param dni DNI del objeto persona
      * @return Cadena de texto con el nombre completo
      */
      public String getFullName(String dni){
      	return "Joaquín Martínez";
      }
      
      /**
      * Obtiene el nombre completo del objeto Persona
      * @param id Identificador del objeto persona
      * @return Cadena de texto con el nombre completo
      */
      public String getFullName(int id){
      	return "Joaquín Martínez";
      }
      

      coment04coment05

Regiones

Otra forma de mantener el código ordenado es mediante regiones, esto nos va a permitir por ejemplo mantener en una clase de negocio los métodos de actualización, adición y eliminación separados de los de selección, mantener propiedades en una región, campos en otra, métodos públicos en otra, privados en otra y así cuantas regiones queramos tener con el fin de mantener el código ordenado. Yo personalmente en una clase, generalizando, siempre tengo una región para constructores y destructores, una para enumeraciones, una para campos, una para métodos públicos y protected, otra para privados y si la clase es un form o window, añado una para controles y dentro de esta, por tipo de control, añado métodos de evento de cada uno, por ejemplo, Buttons, DataGrid, etc.

C#

#region Métodos públicos
        // aquí podemos poner todos los
        // miembros de clase que queramos
#endregion

VB.NET

#Region "Métodos Públicos"
' aquí podemos poner todos los
' miembros de clase que queramos
#End Region

def45-7-2-1
Java

// <editor-fold desc="Métodos públicos" defaultstate="collapsed">
        // aquí podemos poner todos los
        // miembros de clase que queramos
// </editor-fold>

coment06.png

Tareas

Visual Studio tiene comentarios de tareas, es decir si incluimos una serie de comentarios específicos (TOKEN) en alguna parte del código con un comentario adicional, en la lista de tareas, podemos acceder a esta localización de código para continuar con la tarea pendiente. Estos Token pueden personalizarse en el Menú Herramientas, Opciones, Lista de tareas. En este ejemplo, he colocado un TOKEN para revisar el contenido del método y en la lista de tareas, al hacer doble click, nos dirigiremos al código en cuestión.

coment07.png

Hay que añadir y es una buena costumbre, documentar las clases con autoría, fechas de creación y modificación, que hace la clase, que función tiene, las modificaciones que se han realizado, etc.

Hacedme caso, un poco tiempo perdido en documentar, nos hará ganar muchísimo tiempo en un futuro.

coment08.png