Serie ficheros virtuales 

 

 

C Ficheros virtuales

 


 

B.I.3 El servicio COMmagic para cuadrados mágicos y sudokus

 

B.I.3.0 Introducción

B.I.3.1 El servicio SRMagic como sintetización algorítmica del programa PMagic de generación de cuadrados mágicos y sudokus

 

 B.I.3.1.1 Esquema de implementación

 B.I.3.1.2 Rutina de generación principal

 B.I.3.1.3 Subrutina de generación de cuadrados mágicos

 B.I.3.1.4 Subrutina de generación de sudokus

 

  B.I.3.1.4.1 Generación de cuadrados latinos mediante un mecanismo de muestreo sin repetición

  B.I.3.1.4.2 Fichero virtual para el mecanismo del muestreo sin repetición

  B.I.3.1.4.3 Rutinas auxiliares generadoras de números aleatorios

 

 B.I.3.1.5 Rutinas de establecimiento del estado de visualización inicial

 B.I.3.1.6 Rutinas de control de visualización y acceso a datos

 

B.I.3.2 El servicio COMmagic como interfaz de SRMagic

 

                                                            ________

 

B.I.3.0 Introducción

 

En la parte II se presentó un programa para generar cuadrados mágicos y sudokus, con un interfaz Visual C++ 6.

 

A fin de incluir un temporizador, mejorar el interfaz e incluir recursos adicionales, se tomará el programa y se extraerá la parte algorítmica para desarrollar la DLL SRMagic bajo MFC, el equivalente a Visual C++ 6 que incorpora el desarrollador de Visual Studio NET.

 

A este servicio se le proporcionará un interfaz COM, COMMagic, que finalmente se utilizará en un programa de presentación escrito en Visual Basic NET.

 

En un capítulo posterior se desarrollará una DDL contenedora .net, CMagic, que se utilizará en el mismo programa Visual Basic que va a utilizar COMMagic; remitimos a ese capítulo para ver el interfaz de presentación resultante, que coincide en ambos modelos.

 

                                                            ________




B.I.3.1 El servicio SRMagic como sintetización algorítmica del programa PMagic de generación de cuadrados mágicos y sudokus

 

El servicio SRMagic resultante de sintetizar Pmagic implementa las siguientes funciones externas:

 

 

Apertura y cierre

 

 La apertura crea los ficheros virtuales de soporte del proceso, que se cierran con la rutina close. Provienen de las rutinas de carga y cierre del programa de pantalla origen

 

   long SRmagic_Open(void);

   short int SRmagic_Close(void);

 

 

Generación

 

 Se encarga de crear un nuevo sudoku o cuadrado mágico de tamaño N impar (usualmente N = 9). Toma el cuerpo central del botón OK de la pantalla origen.

 

   void SRmagic_Gen(short int m_Sudoku, long N);

 

 

Informativas

 

 Permiten conocer el nº de ítems visibles u ocultos (de 1 a N^2) así como  la suma de ítems visibles por fila o columna. Se han extraído de las rutinas de presentación de la pantalla inicial.

 

   long SRmagic_NumActivas(void);           

   long SRmagic_NumDesactivas(void);    

   long SRmagic_SumaColumna(long N, long j);

   long SRmagic_SumaFila(long N, long i);

 

 

Control de visualización de una celda (i,j)

 

 Tomadas también del control de presentación de resultados de Pmagic.

 

   long SRmagic_Activar(long i, long j);

   long SRmagic_DesActivar(long i, long j);

   short int SRmagic_VerActiva(long i, long j);

 

 

Acceso a datos

 

 Permiten recuperar los datos de una celda (su valor k y su estado de activación a), por acceso directo a una celda (i,j) o secuencial, por el número de orden solicitado.

 

Antiguas rutinas internas de la pantalla de partida, que se han externizado para facilitar el control de presentación en los programas de llamada.

 

   long SRmagic_Chain(long i, long j, long *k, short int *a);

   short int SRmagic_Read(long l, long *i, long *j, long *k, short int *a);

   short int SRmagic_ReadActiva(long l, long *i, long *j, long *k);

   short int SRmagic_ReadDesactiva(long l, long *i, long *j, long *k);

 

 

Auxiliares

 

Provenientes fragmentos recogidos de la codificación original que se han externizado.

 

   char* SRmagic_Edit(long k);        // Edita k en código Z (Sin ceros)

   long SRmagic_RandImpar(long N);    // Genera nº random impar de 1 a N

   long SRmagic_RandCuadrado(long N); // Genera nº random n*n de 1 a N*N

   long SRmagic_Random(long N);       // Genera nº random de 1 a N

 

 

Internas

 

Trascripción directa de sus homólogas originales.

 

   long PrActivarEnColumna(long N, long j);

   long PrActivarEnFila(long N, long i);

   long PrChain(long i, long j);

   long PrChainP(long i, long j);

   long PrChainR(long k);

   short int PrConvierteijIJ(long N, long i, long j, long *I, long *J);

   long PrDelete(long i, long j);

   long PrDeleteP(long i, long j);

   long PrDeleteR(long k);

   long PrDup(long N, long I, long J, long c, long i, long j, long p, long q);

   short int PrGarantizaValorSucesion(long N, long k);

   short int PrGeneraMagic(long N);

   short int PrGeneraSudoku(long N);

   short int PrIJ(long N, long i, long j, long *i1, long *j1);

   long PrMin(long a, long b);

   short int PrNswap(long N, long *Org, long *Des);

   long PrNumActivasSubcubo(long N, long I, long J);

   short int PrRandActiva(long N);

   short int PrRefinoActivas(long N);

   short int PrRefinoActivasDesactivar(long N);

   short int PrRefinoActivasSubcubo(long N, long I, long J);

   short int PrRefinoActivasSucesion(long N);

   long PrSeteq(long i, long j);

   short int PrGenSudoku(long N);

   short int PrSudoku(long N);

   short int PrSudokuIJ(long N, long I, long J, long c);

   short int PrSudokuJI(long N, long I, long J, long c);

   short int PrSudokuRefino(long N, short int *iFacil);

   short int PrSudokuRefinoi(long N, long i, long ii);

   short int PrSudokuRefinoj(long N, long j, long jj);

   short int PrVerFacil(long N);

   short int PrVerValorActivoEnSubcubo(long N, long I, long J, long k);

   long PrWrite(long i, long j, long k, short int a);

   long PrWriteP(long i, long j);

   long PrWriteR(long k);

   short int PrSWAP(long N, short int iSudoku);

   short int PrSWAPC(long N, long j1, long j2);

   short int PrSWAPF(long N, long i1, long i2);

   short int PrSwapIJ(long N, long i1, long j1, long i2, long j2);

 

                                                            ________



B.I.3.1.1 Esquema de implementación

 

La implementación principal derivada de la original de Prgen consiste en generar el fichero virtual “PMAGIC” en formato Cuadrado mágico o Sudoku y luego aplicar un refinado de desactivación de la visualización, todo ello controlado desde la rutina principal SRmagic_Gen que puede resumirse de la forma siguiente para los sudokus:

 

 

Inicio:

 

 SRmagic_Open

 

 

Ciclo:

 

 SRmagic_Gen

 

  PrGeneraSudoku

    PrGenSudoku    // Cuadrado latino núcleo del Sudoku

    PrSudoku       // Despliegue del núcleo como Sudoku trivial

    PrSWAP         // Refino básico por intercambio de filas/columnas

    PrSudokuRefino // Refino principal para pasar de sudoku trivial a usual

 

  PrRefinoActivas  // Refino de visualización de celdas activas

     PrRefinoActivasDesactivar // Desactivación base

     PrRefinoActivasSucesion   // No permite sucesión incompleta 1..N

     SRmagic_SumaFila          // No permite filas nulas      

       PrActivarEnfila         // Activa un ítem en fila

     SRmagic_SumaColumna       // No permite columnas nulas

       PrActivarEnColumna      // Activa un ítem en columna

 

 

Fin:

 

 SRmagic_Close

 

 

En cada ciclo, el contenido de “PMAGIC” queda entonces disponible para su presentación en el programa interfaz elegido, utilizando las rutinas de acceso a datos y control de visualización.

 

 

Este esquema se codifica como sigue:

                                                            ________

  

B.I.3.1.2 Rutina de generación principal 

 

   //--------------------------------------------------------------------------------

   // SRmagic_Gen

   //

   //  Rutina de generación principal de SRmagic

   //

   //  SRmagic_Gen regenera en cada invocación el contenido del fichero virtual

   //             “PMAGIC” con un nuevo sudoku o un nuevo cuadrado mágico

   //              según los parámetros de solicitud

   //

   //  Precondición:

   //   Debe haberse ejecutado previamente SRmagic_Open para crear el fichero

   //   virtual de soporte

   //

   //  Postcondición:

   //   Tras su ejecución “PMAGIC” queda cargado con un nuevo sudoku, o con un nuevo

   //   cuadrado mágico, cuyos datos son accesibles con las funciones de acceso a datos

   //    

   //    SRmagic_Chain, SRmagic_Read, SRmagic_ReadActiva, SRmagic_ReadDesactiva

   //

   //

   //   que se complementan con las funciones de control de visualización

   //

   //    SRmagic_Activar, SRmagic_DesActivar, SRmagic_VerActiva

   //

   //

   //  Parámetros de entrada:

   //

   //    short int m_Sudoku: 0/1 Marca de solicitud de resultado sudoku

   //                        0 = Se solicita un cuadrado mágico

   //                        1 = Se solicita un sudoku

   //

   //    long m_N: Tamaño del cuadrado que solicita (9, 25, 49, ...) lo habitual es 9

   //--------------------------------------------------------------------------------

   void SRmagic_Gen(short int m_Sudoku, long m_N)

   {

    short int er = 0;     // Control de errores

 

    // Construye un nuevo cuadrado de ese tamaño en el fichero “PMAGIC”

    if (m_Sudoku) er = PrGeneraSudoku(m_N); // De tipo sudoku ó

    else          er = PrGeneraMagic(m_N);  // de tipo cuadrado mágico

 

    // Refina visualización, dejando visibles sólo las celdas suficientes para jugar

    er = PrRefinoActivas(m_N);

 

    return;

   }

 

La rutina central invoca entonces a las rutinas auxiliares siguientes, muy similares a las presentadas en la versión de pantalla directa:

 

                                                            ________

 

 

B.I.3.1.3 Subrutina de generación de cuadrados mágicos

 

   //-----------------------------------------------------------------------------

   // Rutina auxiliar de generación de muestras tipo cuadrado mágico

   //-----------------------------------------------------------------------------

   short int PrGeneraMagic(long N)

   {

    long i = 0; // Indice i en curso

    long i1= 0; // Indice i+1 en curso

    long j = 0; // Indice j en curso

    long j1= 0; // Indice j+1 en curso

    long k = 0; // Indice 1..N*N  

 

    short int er = 0;     // Control de errores

    short int activa = 0; // 0/1 celda activa

    long lresul = 0;      // Resultados SRRCW

 

    // Inicia un nuevo fichero virtual de soporte

    er = SRRCW_CLRF("PMAGIC");

 

    // 1er.ítem Fila 1, columna N/2 + 1

    i = 1;

    j = N/2 + 1;

    activa = PrRandActiva(N);

    lresul = PrWrite(i, j, 1, activa);

 

    // Filtro de proceso no trivial

    if (N <= 1) return NO_ERRO;

 

    // Ciclo de proceso

    for (k=2;k<=N*N;++k)

    {

 

     // Movimiento

     er = PrIJ(N, i, j, &i1, &j1);

 

     // Si un movimiento lleva a una celda ocupada, se pondrá k+1 en (i+1, j)

     lresul = PrSeteq(i1, j1);

     if (lresul)

     {

      i1 = i+1;

      j1 = j;

     }

 

     // Graba movimiento en curso

     activa = PrRandActiva(N);

     lresul = PrWrite(i1, j1, k, activa);

 

     // Actualiza contadores

     i = i1;

     j = j1;

    }

 

    return NO_ERRO;

   }

                                                            ________

 

B.I.3.1.4 Subrutina de generación de sudokus

 

   //-----------------------------------------------------------------------------

   // Rutina de generación de sudokus

   //-----------------------------------------------------------------------------

   short int PrGeneraSudoku(long N)

   {

    short int er = 0;     // Control de errores

    short int iFacil = 0; // 0/1 el sudoku generado es de tipo fácil, tiene parejas

                          // repetidas predecibles

    long lResul = 0;      // Resultado de SRmagic_Random

    long n = (long) sqrt((double)N); // Total de supercubos, dimensión local

 

    // Generación del subcubo base

    er = PrGenSudoku(n);  // Núcleo del sudoku. Cuadrado latino

    if (er) return er;

 

    // Rutina de expansión del núcleo Sudoku

    er = PrSudoku(n); // Expansión

    if (er) return er;

 

    er = PrSWAP(N, 1); // Intercambio

    if (er) return er;

 

    er = PrSudokuRefino(N, &iFacil); // Refinado y evaluación

    if (er) return er;

 

    // Una de cada 100 veces en media no discrimina resultado fácil

    lResul = SRmagic_Random(100);

    if (lResul <= 1) return NO_ERRO;

 

    // Reintenta hasta conseguir uno difícil

    if (iFacil) return PrGeneraSudoku(N);

 

    // Intercambio final aleatorio

    if (lResul <= 75)

    {

     er = PrSWAP(N, 1); 

     if (lResul <= 50)

     {

      er = PrSWAP(N, 1); 

      if (lResul <= 25) er = PrSWAP(N, 1); 

     }

    }

 

    // Fin de proceso

    return NO_ERRO

   }

 

Que a su vez invoca a la rutina de generación de cuadrados latinos base del sudoku, que exponemos en mayor detalle que el dedicado a su homóloga de la versión de pantalla directa, para sacar a la luz el mecanismo de obtención de muestras sin repetición en el que se basa la contrucción de cuadrados latinos base para el sudoku, y que consiste precisamente en la utilización de un fichero virtual muy sencillo:

 

                                                            ________


 

B.I.3.1.4.1 Generación de cuadrados latinos mediante un mecanismo de muestreo sin repetición

 

   //-----------------------------------------------------------------------------

   // Rutina auxiliar de generación de muestras tipo cuadrado latino sudoku base

   //-----------------------------------------------------------------------------

   short int PrGenSudoku(long N)

   {

    long i = 0;   // Indice i en curso

    long j = 0;   // Indice j en curso

    long k = 0;   // Indice 1..K  

    long K = N*N; // Indice N*N .. 1

 

    short int activa = 0; // 0/1 celda activa

    short int er = 0;     // Control de errores

    long lresul = 0;      // Resultados SRRCW

 

    // Inicia nuevos ficheros virtuales de soporte

    er = SRRCW_CLRF("PMAGIC");  // Soporte sudoku

    er = SRRCW_CLRF("PMAGICR"); // Soporte de generación de muestras aleatorias sin

                                // repetición (Ver detalle más adelante)

 

    // Ceba la estructura contador

    for (i=1;i<=K;++i) lresul = PrWriteR(i);

 

    // ciclo de generación

    for (i=1;i<=N;++i)

     for (j=1;j<=N;++j)

     {

      // Recupera un nº aleatorio no repetido y lo graba en el cuadro

  

      k = SRmagic_Random(K);

      lresul = PrChainR(k);

      activa = PrRandActiva(N);

      lresul = PrWrite(i, j, Kc.lk, activa);

 

      // Elimina ítem de lista consumido para garantizar la unicidad

     

      --K;

      lresul = PrDeleteR(Kc.lk);

     }

 

    return NO_ERRO;

   }

 

                                                            ________

 

 

B.I.3.1.4.2 Fichero virtual para el mecanismo del muestreo sin repetición

 

La generación de muestras aleatorias sin repetición hace uso de un fichero virtual que utiliza la siguiente estructura de soporte de mínima expresión:

 

   // Ds de lista de nºs para muestras aleatorias sin repetición

   struct sK   // Estructura de claves de acceso a ítem k

   {

    long lk;   // Ítem k

   } Kc;       // Work paso de datos PrChainR

 

En SRmagic_open se crea el fichero virtual para generación de muestras “PMAGICR” utilizando esta definición de soporte:

   lNID = SRRCW_NEW("PMAGICR", sizeof(sK));

 

El fichero se ceba PrGenSudoku con la sentencia

 

   // Ceba la estructura contador

    for (i=1;i<=K;++i) lresul = PrWriteR(i); [ K = N*N ]

 

 

que utiliza la función:

 

   //-----------------------------------------------------------------------------

   // Rutina auxiliar de grabación de un ítem contador

   //-----------------------------------------------------------------------------

   long PrWriteR(long k)

   {

    long lresul = 0; // Control de resultados

    sK dK;           // Ds work paso de datos

 

    // Clave & datos

    dK.lk = k;

    SRRCAL_lLexi(&dK.lk); // Valor a peso

 

    // Graba ítem

    return SRRCW_WRITE("PMAGICR", &dK);

   }

 

Una vez cebado, se descarga con la combinación de sentencias siguientes:

 

   k = SRmagic_Random(K);   // Recupera un nº aleatorio

   lresul = PrChainR(k);    // Extrae ítem de fichero de muestras

   . . .

   --K;                     // Disminuye nº ítems disponible N*N, N*N-1, N*N-2, ...                   

   lresul=PrDeleteR(Kc.lk); // Elimina ítem consumido para garantizar la unicidad

   . . .

 

donde se utilizan las funciones auxiliares de recuperación y eliminación de muestras que vienen a continuación:

 

   //-----------------------------------------------------------------------------

   // Rutina auxiliar de recuperación de un ítem de lista ordenada

   //-----------------------------------------------------------------------------

   long PrChainR(long k)

   {

    short int er = 0; // Control de resultados

 

    // Recupera el k-ésimo ítem disponible en orden creciente

    er = SRRCW_READ("PMAGICR", k, &Kc);

 

    // Traducción a importe de retorno

    SRRCAL_lLexi(&Kc.lk);

    return Kc.lk;

   }

 

   //-----------------------------------------------------------------------------

   // Rutina auxiliar de eliminación de un ítem de reordenación

   //-----------------------------------------------------------------------------

   long PrDeleteR(long k)

   {

    sK dK; // Ds work paso de datos

 

    // Clave (Recuperada anteriormente en PrChainR)

    dK.lk = k;  

    SRRCAL_lLexi(&dK.lk);

 

    // Elimina el k-ésimo ítem en orden consumido

    return SRRCW_DELETE("PMAGICR", &dK);

   }

                                                            ________

 

 

B.I.3.1.4.3 Rutinas auxiliares generadoras de números aleatorios

 

Los procedimientos presentados utilizan una familia de rutinas auxiliares generadoras de números aleatorios que se han externizado.

Estas rutinas están construidas en cascada, apoyándose cada una en la anterior.

En primer lugar se encuentra el generador de números aleatorios enteros SRmagic_Random:

   //-----------------------------------------------------------------------------

   // Rutina auxiliar de generación de nºs aleatorios enteros

   //

   // Parámetro de entrada: Número N frontera superior para la generación

   //

   // Retorno: Número aleatorio K en el rango 1 .. N (Ambos inclusive)

   //-----------------------------------------------------------------------------

   __declspec(dllexport) long SRmagic_Random(long N)

   {

    long lresul = 0;    // Resultado

    double dRand = 0.0; // Rand utilizado (0,1)

 

    // Genera un nº aleatorio entre 0 y 1 (Ambos exclusive)

    dRand = (double) rand() / (double) RAND_MAX;

   

    // Convierte el nº aleatorio entre 0 y 1 en un entero entre 1 y N

    lresul = (long) ( (double) N * dRand ) + 1;

    if (lresul > N) lresul = N;

 

    return lresul;

   }

 

 

Disponiendo del generador de números aleatorios enteros, se aplica como base para construir un generador de números aleatorios enteros impares:

 

   //-----------------------------------------------------------------------------

   // Genera un tamaño de subcuadrado aleatorio, de tamaño impar

   //

   // Parámetro de entrada: Número NMAX (impar) frontera superior para la generación

   //

   // Retorno: Número aleatorio impar N en el rango 1 .. NMAX (Ambos inclusive)

   //

   // (Nota importante: Lo propio es que NMAX sea impar, pero si es par, N sería un

   //                   número impar en el rango  1 .. NMAX+1 )

   //

   //-----------------------------------------------------------------------------

   __declspec(dllexport) long SRmagic_RandImpar(long lNMAX)

   {

    long m_N = 0; // Valor de retorno

    m_N = 1 + 2 * (SRmagic_Random(lNMAX) / 2);

    return m_N;

   }

 

Rutina con la que puede construirse la siguiente generadora de números cuadrados impares:

 

   //-----------------------------------------------------------------------------

   // Genera un tamaño de cuadrado aleatorio, de tamaño impar

   //

   // Parámetro de entrada: Número cuadrado NMAX (impar) frontera superior para la generación

   //

   // Retorno: Número aleatorio cuadrado impar N en el rango 1 .. NMAX (Ambos inclusive)

   //          N es de la forma n*n con n también impar

   //

   // Nota importante:

   //  Lo propio es que NMAX sea impar, pero si es par, N sería un número cuadrado impar

   //  en el rango  1 .. NMAX – 1

   //  [ó -3, ó -5 .. el que sea el cuadrado impar más cercano inferiormente a NMAX]

   //

   //-----------------------------------------------------------------------------

   __declspec(dllexport) long SRmagic_RandCuadrado(long lNMAX)

   {

    long m_N = 0; // Valor de retorno

 

    // Obtiene nmax = sqrt(NMAX)

    long nmax = (long) sqrt((double)lNMAX); // Raíz de lNMAX

 

    // Genera numero aleatorio impar base y devuelve su cuadrado

    m_N = SRmagic_RandImpar(nmax);

    m_N *= m_N;

 

    return m_N;

   }

 

El resto de rutinas auxiliares se han reservado para uso interno y son un calco de las utilizadas en la versión de pantalla directa. Su significado es más obvio, y pueden consultarse en detalle acudiendo a sus fuentes completos.

 

Tras haber repasado las funciones de generación, centrémonos ahora en la segunda parte, su presentación, haciendo uso del siguiente juego de rutinas

                                                            ________

 

B.I.3.1.5 Rutinas de establecimiento del estado de visualización inicial

 

Para la visualización inicial del resultado se utiliza una rutina final de refinado de celdas activas que involucra unas reglas mucho más simples que la propia generación del sudoku, pero cuyo detalle de implementación, al involucrar bucles en cubos y subcubos buscando dejar entrever más que mostrar resulta en ciertos puntos más prolija que la dedicada a los algoritmos principales.

 

Las ideas centrales de esta última parte se marcan en los comentarios resaltados del código que se presenta a continuación:

 

    //-----------------------------------------------------------------------------

    // Rutina auxiliar de refinado de activaciones

    //-----------------------------------------------------------------------------

    short int er = 0;  // Control de errores intermedios

    long lresul = 0;   // Control de resultados intermedios

    long i = 0, j = 0; // Contadores de for. Filas, columnas cubo

    long I = 0, J = 0; // Contadores de for. Filas, columnas subcubos

    long si= 0, sj= 0; // Id.en subcubo

    long Nrandom = 0;  // Random auxiliar 1..N

 

    long ContadornActivas = 0;       // Contador de subcubos con n activas

    long NumActivasSubcubo = 0;      // Contador de activas en subcubo

    long n = (long) sqrt((double)N); // Total de supercubos, dimensión local

 

    // Genera random auxiliar 1..N

    Nrandom = SRmagic_Random(N);

 

    // Desactivación base. Limpia diagonales y subdiagonales

    er = PrRefinoActivasDesactivar(N);

 

    // No permite sucesiones incompletas

    er = PrRefinoActivasSucesion(N);

 

   // No permite filas nulas

    for (i=1;i<=N;++i)

    {

     lresul = SRmagic_SumaFila(N, i);

     if (!lresul) lresul = PrActivarEnFila(N, i);

    }

 

 

    // No permite columnas nulas

    for (j=1;j<=N;++j)

 

    {

     lresul = SRmagic_SumaColumna(N, j);

     if (!lresul) lresul = PrActivarEnColumna(N, j);

    }

   

    // No permite subcubos nulos ni muy densos

    for (I=1;I<=n;++I) for (J=1;J<=n;++J) er = PrRefinoActivasSubcubo(N, I, J);

 

 

   // No permite filas nulas (2ª pasada)

    for (i=1;i<=N;++i)

    {

     lresul = SRmagic_SumaFila(N, i);

     if (!lresul) lresul = PrActivarEnFila(N, i);

    }

 

    // No permite columnas nulas (2ª pasada)

    for (j=1;j<=N;++j)

    {

     lresul = SRmagic_SumaColumna(N, j);

     if (!lresul) lresul = PrActivarEnColumna(N, j);

    }

 

    // Al menos n+1 subcubos deben tener n celdas activas

    for (I=1;I<=n;++I)

     for (J=1;J<=n;++J)

     {

      NumActivasSubcubo = PrNumActivasSubcubo(N, I, J);

      if (NumActivasSubcubo >= n)

      {

       ++ContadornActivas;

       if (ContadornActivas >= n+1) return NO_ERRO;

      }

     }

 

    // Activa el nº suficiente en diagonales para cumplir la regla anterior

    // (Permitiendo n+1 activos la mitad de las veces y n el resto)

    for (J=1;J<=n;++J)

    {

     if (Nrandom <= N/2)

     {

      for (si=1;si<=n;++si)

      {

       SRmagic_Activar(si + n*(J-1), 1 + n*(J-1));

       NumActivasSubcubo = PrNumActivasSubcubo(N, J, J);

       if (NumActivasSubcubo >= n) break;

      }

     }

     else

     {

      for (sj=1;sj<=n;++sj)

      {

       SRmagic_Activar(1 + n*(J-1), sj + n*(J-1));

       NumActivasSubcubo = PrNumActivasSubcubo(N, J, J);

       if (NumActivasSubcubo >= (n+1)) break;

      }

     }

    }

 

   // Fin de refino satisfactorio

   return NO_ERRO;

  }

                                                            ________

 

La rutina núcleo de PrRefinoActivas es la particularización para subcubos que sigue:

 

   //-----------------------------------------------------------------------------

   // No permite subcubos nulos ni muy densos

   //-----------------------------------------------------------------------------

   short int PrRefinoActivasSubcubo(long N, long I, long J)

   {

    short int er = 0;    // Control de errores

    short int leave = 0; // Control de leave

    long lresul = 0;     // Control de resultados intermedios

    long si = 0, sj = 0; // Contadores de for. Filas, columnas en subcubo

    long II=0,JJ=0;      // Contadores for en subcubos

    long l = 0;          // Contador de for

    long lexceso = 0;    // Contador de excesos

    long Nrandom = 0;    // Random auxiliar 1..N

 

    long NumActivasSubcubo = 0;      // Contador de celdas activas en el subcubo

    long n = (long) sqrt((double)N); // Total de supercubos, dimensión local

 

    // Bucle de evaluación de celdas activas. Ha de haber alguna activa

    NumActivasSubcubo = PrNumActivasSubcubo(N, I, J);

    if (!NumActivasSubcubo)

    {

     // Activa la penúltima celda del subcubo en caso de no tener ninguna activa

     lresul = SRmagic_Activar(n*I, n*J - 1);

     return NO_ERRO;

    }

 

    // Si el nºde activas es grande (>n ó >(n+1)), lo depura

    Nrandom = SRmagic_Random(N);

    if (Nrandom > N/2) if (NumActivasSubcubo <= n)     return NO_ERRO;

    else               if (NumActivasSubcubo <= (n+1)) return NO_ERRO;

 

    // Exceso a depurar

    if (Nrandom > N/2) lexceso = 1 + NumActivasSubcubo - n;

    else               lexceso =     NumActivasSubcubo - n;

    for (l=1;l<=lexceso;++l)

    {

     leave = 0;

     // Busca celdas que depurar en el subcubo en curso

     for (si=1;si<=n;++si)

     {

      for (sj=1;sj<=n;++sj)

      {

       lresul = SRmagic_VerActiva(si + n*(I-1), sj + n*(J-1));

       if (!lresul) continue;

 

       // Recupera el valor asociado de la celda en curso

       lresul = PrChain(si + n*(I-1), sj + n*(J-1));

 

       // Evalua el valor obtenido en todos los demas subcubos

       for (II=1;II<=n;++II)

       {

        for (JJ=1;JJ<=n;++JJ)

        {

         if (II==I && JJ==J) continue; // Se salta el de partida

         lresul = PrVerValorActivoEnSubcubo(N, II, JJ, DSc.DD.lk);

         if (!lresul) continue; // No activo en II, JJ

 

         // Si lo encuentra (repetido), lo desactiva

         lresul = SRmagic_DesActivar(si + n*(I-1), sj + n*(J-1));

         leave = 1;

         break;

        }

        if (leave) break;

       }

       if (leave) break;

      }

      if (leave) break;

     }

    }

 

    // Fin de proceso satisfactorio

    return NO_ERRO;

   }

                                                            ________

 

 

B.I.3.1.6 Rutinas de control de visualización y acceso a datos

 

Tras la visualización de partida, la presentación irá evolucionando hasta mostrar el resultado completo.

 

Para ello se provee de los controles SRmagic_Activar, SRmagic_DesActivar y SRmagic_VerActiva y de las rutinas de acceso a datos SRmagic_Chain, SRmagic_Read, SRmagic_ReadActiva y SRmagic_ReadDesactiva.

 

Veamos por ejemplo el código de la rutina que permite leer las celdas visualmente activas:

 

   //-----------------------------------------------------------------------------

   // SRmagic_ReadActiva: Recuperación de una celda activa del cuadro

   //

   //

   // Precondición:

   //

   // Fichero PMAGIC soporte de la aplicación cargado en SRmagic_Gen

   //

   //

   // Postcondición:

   //

   // Datos de la celda activa solicitada disponibles

   //

   //

   // Parámetros de entrada: Indice secuencial de lectura en el fichero lógico

   //                        “PMAGIC02” (=Lógico que reordena las celdas de forma que

   //                         las visualmete activas se presenten en primer lugar)

   //

   // Parámetros de salida : Coordenadas i,j de la celda leída

   //                        k=Valor de la celda

   //

   // Retorno: 0/1 Marca de error

   //          Si es 1 se desprende que no existía el ítem solicitado como

   //          visualmente activo

   //

   //-----------------------------------------------------------------------------

   short int SRmagic_ReadActiva(long l, long *i, long *j, long *k)

   {

    short int er = 0; // Control de resultados SRRCW

 

    // Acceso

    er = SRRCW_READ("PMAGIC02", l, &DSc);

    if (er) return er;

 

    // Filtro. La celda debe estar activa

    if (!DSc.DD.a) return 1;

 

    // Paso de resultados

    SRRCAL_lLexi(&DSc.DK.li);  // Paso de peso a valor en la estructura de claves

    SRRCAL_lLexi(&DSc.DK.lj);

 

    *i = DSc.DK.li;

    *j = DSc.DK.lj;

    *k = DSc.DD.lk;

 

    return NO_ERRO;

   }

 

   el resto de funciones de control de visualización tienen un funcionamiento y código similar, y pueden encontrarse en el fuente completo que se encuentra en el apéndice

"C5 Relación de programas de muestra desarrollados en el libro"

 

                                                            ________

 

 

 

B.I.3.2 El servicio COMmagic como interfaz de SRmagic

 

Para SRmagic se puede definir COMmagic con un interfaz estandarizado ISRmagic apto para la familia de lenguajes MS con los siguientes métodos para acceso a las funciones de SRmagic:

 

__interface ISRmagic : IDispatch

{

 [id(1), helpstring("method Open")] HRESULT Open([out] LONG* lNID);

 [id(2), helpstring("method Close")] HRESULT Close([out] LONG* lError);

 [id(3), helpstring("method Activar")] HRESULT Activar([in] LONG i, [in] LONG j, [out] LONG* lResul);

 [id(4), helpstring("method Desactivar")] HRESULT Desactivar([in] LONG i, [in] LONG j, [out] LONG* lResul);

 [id(5), helpstring("method Chain")] HRESULT Chain([in] LONG i, [in] LONG j, [out] LONG* lValor,

                                                   [out] LONG* lEstado, [out] LONG* lResul);

 [id(6), helpstring("method Edit")] HRESULT Edit([in] LONG lValor, [out] BSTR* bEditado);

 [id(7), helpstring("method Gen")] HRESULT Gen([in] LONG lSudoku, [in] LONG lN);

 [id(8), helpstring("method SumaColumna")] HRESULT SumaColumna([in] LONG lN, [in] LONG j, [out] LONG* lSumaColumna);

 [id(9), helpstring("method SumaFila")] HRESULT SumaFila([in] LONG lN, [in] LONG i, [out] LONG* lSumaFila);

 [id(10), helpstring("method VerActiva")] HRESULT VerActiva([in] LONG i, [in] LONG j, [out] LONG* lActiva);

 [id(11), helpstring("method RandCuadrado")] HRESULT RandCuadrado([in] LONG lNMAXcuadrado,

                                                                  [out] LONG* lRandCuadrado);

 [id(12), helpstring("method RandImpar")] HRESULT RandImpar([in] LONG lNMAX, [out] LONG* lRand);

 [id(13), helpstring("method Random")] HRESULT Random([in] LONG lN, [out] LONG* lRandom);

 [id(14), helpstring("method ReadDesactiva")] HRESULT ReadDesactiva([in] LONG lIndiceLectura, [out] LONG* i,

                                                      [out] LONG* j, [out] LONG* lValorCelda, [out] LONG* lErro);

 [id(15), helpstring("method NumActivas")] HRESULT NumActivas([out] LONG* NumeroActivas);

 [id(16), helpstring("method NumActivasFila")] HRESULT NumActivasFila([in] LONG i, [out] LONG* NumeroActivas);

 [id(17), helpstring("method NumActivasColumna")] HRESULT NumActivasColumna([in] LONG j, [out] LONG* NumeroActivas);

 [id(18), helpstring("method NumDesactivas")] HRESULT NumDesactivas([out] LONG* NumeroDesactivas);

 [id(19), helpstring("method ReadActiva")] HRESULT ReadActiva([in] LONG lIndice, [out] LONG* i, [out] LONG* j,

                                                                   [out] LONG* lValorCelda, [out] LONG* lError);

 [id(20), helpstring("method Read")] HRESULT Read([in] LONG lIndiceLectura, [out] LONG* i, [out] LONG* j,

                                              [out] LONG* lValorCelda, [out] LONG* lEstado, [out] LONG* lError);

 

};

 

Para construir este interfaz hay que proceder de la misma forma que se siguió  en el capítulo dedicado al servicio COMw, interfaz COM de SRRCW para uso en Visual Basic.

 

 

                                                   ________