Uso cookies propias y de terceros. Si continuas navegando consideraré que aceptas el uso de cookies. OK Más información

Envía un mensaje o llámame636239261

hola@israelnoguera.com

Presupuesto web Presupuesto web

Curso Desarrollo Web: Sudoku parte 1

  • Dificultad: Alta
  • Tecnologías: JavaScript, Canvas Html5
  • Demo: Sudoku
Sudoku
SUDOKU

Vamos a ver en este ejercicio de desarrollo web, como crear un Sudoku. Tal como nos cuenta la Wikipedia, el objetivo del sudoku es rellenar una cuadrícula de 9×9 celdas (81 casillas) dividida en subcuadrículas de 3×3 (también llamadas "cajas" o "regiones") con las cifras del 1 al 9 partiendo de algunos números ya dispuestos en algunas de las celdas. Lo que importa, es que sean nueve elementos diferenciados, que no se deben repetir en una misma fila, columna o subcuadrícula.

La representación gráfica del Sudoku la haré con la ayuda del Canvas de Html5, por sencillez. Así que lo primero que voy a hacer es crear gráficamente la cuadrícula del Sudoku.

<canvas id="myCanvas" style="margin:auto;border:3px solid #000000;"></canvas>

Y ahora pintar sobre el canvas la cuadrícula.

var w = window.innerWidth;
var h = window.innerHeight;
var canvasLength = 0;

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

/*
La longitud del canvas será la mitad de la anchura o altura de la ventana disponible
*/
canvasLength = w >= h ? h/2 : w/2;
c.width = canvasLength;
c.height = canvasLength;
	
var cellHeight = canvasLength/9; // Longitud de una celda

for(i=1;i<=8;i++){ 
  //Pintado de líneas horizontales
  ctx.beginPath();
  ctx.moveTo(0, cellHeight*i);
  if(i%3==0){
    ctx.lineWidth = 4;
  }else{
    ctx.lineWidth = 1;
  }						
  ctx.lineTo(canvasLength, cellHeight*i);
  ctx.stroke();	

  //Pintado de líneas verticales
  ctx.beginPath();
  ctx.moveTo(cellHeight*i, 0);
  ctx.lineTo(cellHeight*i, canvasLength);
  ctx.lineWidth = 1;
  if(i%3==0){
    ctx.lineWidth = 4;
  }else{
    ctx.lineWidth = 1;
  }				
  ctx.stroke();
}

Ver como queda la cuadrícula sobre el canvas

En esta parte del código se genera el contexto 2d del canvas para trabajar sobre el. Se le da unas dimensiones basadas en el ancho y alto de la ventana del navegador disponible y para no hacerlo muy grande, se divide entre dos. Se trazan las líneas horizontales y verticales, sólo son necesarias 8 porque el marco del canvas ya está pintado.

Cada 3 líneas, la línea se pinta con un grosor más ancho, para marcas la regiones de 3x3 más claramente. Esa operación la podemos ver donde se opera con el módulo de 3 (i%3) en el bucle.

Ahora hay que rellenar las celdas con los valores adecuados, pero antes de ello, ya que es complejo, lo haré con valores temporales no válidos, con un cero.

for(ri=1;ri<10;ri++){    
  for(ci=0;ci<9;ci++){ 					
    ctx.fillStyle = "#000000";
    ctx.font = "30px Arial";
    ctx.fillText(0, (ci*cellHeight)+cellHeight/3, (ri*cellHeight)-(cellHeight/3)+4);
  }
}

Ver como queda la cuadrícula sobre el canvas rellenada con ceros

No hay mucho que destacar en esta parte, dedicarle un rato para entender el doble bucle y cómo va completando las celdas que hay pintadas.

Ahora viene la parte más compleja, ahora hay que sustituir esos ceros por valores reales. Dedícale un rato a pensar cómo lo harías.

Efectivamente hay varias maneras de hacerlo, yo decidí hacerlo de izquierda a derecha y de arriba abajo, veámoslo.

Sudoku cargado con números

Podemos ver como se han cargado con números y además por cada fila no se repite ningún número. ¡Bien! esto era necesario, pero se repiten algunos número en vertical y en las regiones de 3x3. Antes de solucionar esto analizemos un poco el código.

// esto lo añadimos en el código existente
var sudoku = Array();
sudoku = loader(sudoku);


// Modificamos esta línea existente, donde se incluían los ceros
ctx.fillText(0, (ci*cellHeight)+cellHeight/3, (ri*cellHeight)-(cellHeight/3)+4);
// Por esta
ctx.fillText(sudoku[ri][ci], (ci*cellHeight)+cellHeight/3, (ri*cellHeight)-(cellHeight/3)+4);


// Nuevas funciones
function loader(sudoku){			
  var row;
  var n;			
  for(ri=1;ri<10;ri++){
    row = Array();            
    for(ci=1;ci<10;ci++){
	  n = getNumber(row);							
	  row.push(n);
    }	
    sudoku[ri] = row;                
  }
  return sudoku;			
}

function getNumber(data){
  var num = Math.floor((Math.random() * 9) + 1);
  if(inArray(num,data)){
    return getNumber(data);			
  }else{
    return num;
  }
}		

function inArray(needle,haystack) {
  var length = haystack.length;
  for(var i = 0; i < length; i++) {
    if(haystack[i] == needle) return true;
  }			
  return false;
} 

En primer lugar, la matríz bidimensional sudoku, va a contener los valores necesarios para rellenar el sudoku. Podemos ver como llama a la función loader(), que se encarga de crear esa estructura bidemensional, la cual se va creando como me interesa; de izquierda a derecha y de arriba abajo, pasando por todas las celdas del sudoku, desde la celda 1 a la celda 9 de la fila 1, para pasar después a la fila 2 y así succesivamente.

Cada vez que el bucle va a cargar un nuevo valor, se llama a la función getNumber(), (se le pasa la matríz row, que contiene todos los valores de cada fila) la cual devuelve un número, que lo asigna a la variable n, esta variable se añade a la matríz temporal row, que contiene todos los números de cada fila. Cada vez que se empieza una nueva fila, esta matríz temporal, se vacía, pero antes de vaciarse, se añade a la matríz sudoku, que se ha pasado por parámetro. Realmente no es necesario pasar por parámetro esta matríz, pero como me hará falta más adelante, ya me va bien así.

Si vemos un poco por encima la función getNumber(), esta lo que hace es devolver un número aleatorio entre 1 y 9, pero como necesitamos que no se repitan por fila, lo revisa precisamente con la función auxiliar inArray().

Cuando se hace la comprobación llamando al inArray() y pasando por parámetro el número y la matríz, vemos que hay dos posibilidades; que el número no exista, por lo cual se devuelve el número, sin mas, o se vuelva a llamar a la función getNumber(), donde se repite el proceso, hasta conseguir un valor que no exista en la matríz data. Evidentemente, esta iteracción se repetirá sobretodo, cuando queden pocos valores para completar la matríz de 9 números de cada fila.

Bien, ahora hay que coger un poco de aire. Completar el sudoku con valores numéricos del 1 al 9 y que no se repitan por fila, ha sido relativamente sencillo, pero ahora que conseguir que no se repitan, además, por columna y por regiones de 3x3.

Voy a mostrar todo el código necesario y le echamos un ojo.

function loader(sudoku){
  var col1=Array(),col2=Array(),col3=Array(),col4=Array(),            
    col5=Array(),col6=Array(),col7=Array(),col8=Array(),            
    col9=Array(),grp1=Array(),grp2=Array(),grp3=Array(),   
    grp4=Array(),grp5=Array(),grp6=Array(),grp7=Array(),            
    grp8=Array(),grp9=Array(),row = Array();
  var n,numbers;

  for(ri=1;ri<10;ri++){    
    row = Array();            
    for(ci=1;ci<10;ci++){   			
      switch(ci){
        case 1:	
          if(ri<=3){
            numbers = row.concat(col1,grp1);
            n = getNumber(numbers.filter(onlyUnique));							
            grp1.push(n);
          }else if(ri<=6){
            numbers = row.concat(col1,grp4);
            n = getNumber(numbers.filter(onlyUnique));															
            grp4.push(n);
          }else if(ri<=9){
            numbers = row.concat(col1,grp7);
            n = getNumber(numbers.filter(onlyUnique));							
           grp7.push(n);
          }							
          col1.push(n);
        break;
        case 2:														
          if(ri<=3){
            numbers = row.concat(col2,grp1);
            n = getNumber(numbers.filter(onlyUnique));							
            grp1.push(n);
          }else if(ri<=6){
            numbers = row.concat(col2,grp4);
            n = getNumber(numbers.filter(onlyUnique));
            grp4.push(n);
          }else if(ri<=9){
            numbers = row.concat(col2,grp7);
            n = getNumber(numbers.filter(onlyUnique));
            grp7.push(n);
          }							
          col2.push(n);
        break;
        case 3:													
          if(ri<=3){
            numbers = row.concat(col3,grp1);
            n = getNumber(numbers.filter(onlyUnique));							
            grp1.push(n);
          }else if(ri<=6){
            numbers = row.concat(col3,grp4);
            n = getNumber(numbers.filter(onlyUnique));							
            grp4.push(n);
          }else if(ri<=9){
            numbers = row.concat(col3,grp7);
            n = getNumber(numbers.filter(onlyUnique));							
            grp7.push(n);
          }							
          col3.push(n);
        break;
        case 4:														
          if(ri<=3){
            numbers = row.concat(col4,grp2);
            n = getNumber(numbers.filter(onlyUnique));							
            grp2.push(n);
          }else if(ri<=6){
            numbers = row.concat(col4,grp5);
            n = getNumber(numbers.filter(onlyUnique));							
            grp5.push(n);
          }else if(ri<=9){
            numbers = row.concat(col4,grp8);
            n = getNumber(numbers.filter(onlyUnique));							
            grp8.push(n);
          }							
          col4.push(n);
        break;
        case 5:														
          if(ri<=3){
            numbers = row.concat(col5,grp2);
            n = getNumber(numbers.filter(onlyUnique));							
            grp2.push(n);
          }else if(ri<=6){
            numbers = row.concat(col5,grp5);
            n = getNumber(numbers.filter(onlyUnique));							
            grp5.push(n);
          }else if(ri<=9){
            numbers = row.concat(col5,grp8);
            n = getNumber(numbers.filter(onlyUnique));							
            grp8.push(n);
          }
          col5.push(n);							
        break;
        case 6:														
          if(ri<=3){
            numbers = row.concat(col6,grp2);
            n = getNumber(numbers.filter(onlyUnique));							
            grp2.push(n);
          }else if(ri<=6){
            numbers = row.concat(col6,grp5);
            n = getNumber(numbers.filter(onlyUnique));							
            grp5.push(n);
          }else if(ri<=9){
            numbers = row.concat(col6,grp8);
            n = getNumber(numbers.filter(onlyUnique));							
            grp8.push(n);
          }							
          col6.push(n);
        break;
        case 7:														
          if(ri<=3){
            numbers = row.concat(col7,grp3);
            n = getNumber(numbers.filter(onlyUnique));							
            grp3.push(n);
          }else if(ri<=6){
            numbers = row.concat(col7,grp6);
            n = getNumber(numbers.filter(onlyUnique));							
            grp6.push(n);
          }else if(ri<=9){
            numbers = row.concat(col7,grp9);
            n = getNumber(numbers.filter(onlyUnique));							
            grp9.push(n);
          }							
          col7.push(n);
        break;
        case 8:														
          if(ri<=3){
            numbers = row.concat(col8,grp3);
            n = getNumber(numbers.filter(onlyUnique));							
            grp3.push(n);
          }else if(ri<=6){
            numbers = row.concat(col8,grp6);
            n = getNumber(numbers.filter(onlyUnique));							
            grp6.push(n);
          }else if(ri<=9){
            numbers = row.concat(col8,grp9);
            n = getNumber(numbers.filter(onlyUnique));							
            grp9.push(n);
          }							
          col8.push(n);
        break;	
        case 9:														
          if(ri<=3){
            numbers = row.concat(col9,grp3);
            n = getNumber(numbers.filter(onlyUnique)); 							
            grp3.push(n);
          }else if(ri<=6){
            numbers = row.concat(col9,grp6);
            n = getNumber(numbers.filter(onlyUnique)); 							
            grp6.push(n);
          }else if(ri<=9){
            numbers = row.concat(col9,grp9);
            n = getNumber(numbers.filter(onlyUnique)); 							
            grp9.push(n);
          }							
          col9.push(n);
        break;						
      }				
      if(n){
        row.push(n)
      }else{						
        return loader(sudoku);
      }
    }	
    sudoku[ri] = row;                
  }
  return sudoku;			
}

function onlyUnique(value, index, self) { 
  return self.indexOf(value) === index;
}

He reescrito la función loader(), para que ahora, además de las filas,  no repita números en columnas y en regiones 3x3.

Al inicio de la función, inicio varias matrices, las cuáles nos pueden dar una idea de la estrategia a seguir... lo que hago es crear una serie de matrices que van a contener los números 'prohibidos', los número que no se van a poder repetir en una determinada columna o los números que no se van a poder repetir en una determinada región, por lo tanto, necesitamos 9 matrices para las columnas y otras 9 para las regiones 3x3.

¿Cómo funciona?

Cuando se rellena el valor de la celda 1 (la primera de todas, arriba a la izquierda), ese valor se almacena en la columna 1 de valores prohibidos, que yo he llamado col1, además, se almacena también en la región 1 de valores prohibidos, que yo he llamado grp1, esta región comprende las tres primeras celdas de la fila 1, de la fila 2 y de la fila 3.

Cuando la iteracción pasa a la celda 2, ese valor se almacena en col2 y en grp1, cuando pasa a la celda 3, se almacena en col3 y en grp1, cuando pasa a la celda 4, se almacena en col4 y en grp2, etc, etc... 

Todo esto es lo que sucede ahora en loader(), en la construcción del array bidimensional, el cual es un poco más complejo.

La matriz row, sigue almacenando los valores no repetidos de cada fila, como hacía anteriormente, pero ahora, cuando busca un nuevo valor, debe tener en cuenta los valores prohibidos para la columna donde está y la región donde está. Antes, esto se hacía pasando la matriz row a getNumber(), y ahora se hace prácticamente igual, pasando esa misma matríz, pero incoporando los nuevos valores que no se pueden repetir, los cuales se concatenan antes de pasarse y se eliminan duplicados gracias al filtro onlyUnique. Ahora el nuevo número obtenido, además de asignarse a row, se asigna a la columna y grupo 'prohibido' que le pertoque.

Hay un detalle importante que se debe tener en cuenta para que esto funcione. Si lo dejáramos así no funcionaría. Podemos ver casi al final, como se llama de nuevo a la función loader() sino se ha conseguido un valor n. ¿Cuál es el problema? el problema es que a medida que se va completando las filas, los valores que se van asignando automáticamente pueden dar lugar a combinaciones imposibles. Cuando se produce esto, es necesario tratar de recargar de nuevo la matriz principal sudoku.

Además de lo comentado en el párrafo anterior, es necesario hacer un cambio importante en la función ya existente getNumber()

function getNumber(data){
  if(data.length>8){
    return 0;
  }

  var num = Math.floor((Math.random() * 9) + 1);
  if(inArray(num,data)){
    return getNumber(data);			
  }else{
    return num;
  }
}

Ahora getNumber() devuelve un cero, cuando la matriz data pasada por parámetro, la cual contiene los valores de una fila ya está cargada con 9 números y por lo tanto no puede cargar ninguno más. Cuando esto sucede se reinicia todo para intentar de nuevo dar con una combinación válida.

Ahora ya está con todos los números, sin repetirse en ninguna fila, columna o región

Acabaré esta primera parte, dejando los huecos en blanco necesarios para poder completar el sudoku, con varios niveles de dificultad.

function getValidCells(difficulty){		

  var values = Array();
  for(ri=1;ri<10;ri++){
    for(ci=0;ci<9;ci++){ 					
      values.push(ri+''+ci);
    }
  }			

  values = shuffle(values);

  var difficultyValue;
  switch(difficulty){
    case 'veryeasy':
      difficultyValue = 70;
    break;
    case 'easy':
      difficultyValue = 60;
    break;
    case 'normal':
      difficultyValue = 50;
    break;
    case 'hard':
      difficultyValue = 40;
    break;
    case 'veryhard':
      difficultyValue = 30;
    break;				
    case 'complex':
      difficultyValue = 17;
    break;				
  }

  for(i=0;i<difficultyValue;i++){
    values.shift();
  }
  return values;
}		

function shuffle(a) {
  var j, x, i;
  for (i = a.length - 1; i > 0; i--) {
    j = Math.floor(Math.random() * (i + 1));
    x = a[i];
    a[i] = a[j];
    a[j] = x;
  }
  return a;
}	

He añadido dos nuevas funciones. shuffle() básicamente lo que hace es modificar el orden de manera aleatoria de todos los valores de una matríz (viene a hacer lo mismo que la función shuffle de php, de ahí que le pongo el mismo nombre). Y getValidCells() lo que hace en primer lugar es crear una matríz con 81 valores, almacenanado como valor el índice de cada fila y columna.

A continuación se mezclan todos estos valores y se eliminan algunos de ellos. Según la dificultad, se eliminan más o menos valores. Como vemos en el bucle, se eliminan los valores al inicio de la matriz, pero como esta mezclada, nunca serán valores correlativos (celdas correlativas en el sudoku).

Ahora veremos, como usar esta matríz para anular algunos valores de la matríz principal:

var difficulty = 'veryeasy'; //veryeasy, easy, normal, hard, veryhard, complex
var defValues = getValidCells(difficulty);

for(ri=1;ri<10;ri++){    
  for(ci=0;ci<9;ci++){ 					
    // intruce los números
    ctx.fillStyle = "#000000";
    ctx.font = "30px Arial";
    ctx.fillText(sudoku[ri][ci], (ci*cellHeight)+cellHeight/3, (ri*cellHeight)-(cellHeight/3)+4);

    // casillas vacias
    ctx.fillStyle = "#FFFFFF";					
    if(inArray(ri+''+ci,defValues)){
      ctx.fillRect(ci*cellHeight+4, (ri*cellHeight+4)-cellHeight, cellHeight-8, cellHeight-8);					
    }
  }
}

En defValues se obtienen precisamente la matríz con los valores que voy a anular. 

Podemos ver en el doble bucle, que ya usé al principio, como ahora, además de imprimir los números sobre el canvas, imprimo unos cuadrados blancos sobre algunos números.

¿Sobre qué números? pues sólo sobre aquellos que correspondan con el índice ri y el índice ci del bucle y que estén almacenados en defValues.

Veámos el resultado final

En estos ejemplos que se desplegan desde el enlace, hay aplicada una dificultad 'veryhard', por lo que se están mostrando 30 números, se deben resolver 51 celdas vacías.

Este ejercicio se entenderá muchísimo mejor viendo todo el código de una sola vez. Espero que te haya sigo de utilidad y no te pierdas la segunda parte, donde le daré funcionalidad al sudoku, para que se pueda completar online.

Aquí dejo disponible todo el ejercicio:

<!doctype html>
<head>
<meta charset="UTF-8">
<title>SUDOKU</title>
<style>
  body{
    background-color:#fff;
	text-align:center;
  }    
  #myCanvas{
    margin:60px auto;
    border:solid 1px #e1e1e1;
  } 
</style>
</head>
<body>
<canvas id="myCanvas" style="margin:auto;border:3px solid #000000;"></canvas>
</body>
<script>       

var w = window.innerWidth;
var h = window.innerHeight;
var canvasLength = 0;

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

canvasLength = w >= h ? h/2 : w/2;
c.width = canvasLength;
c.height = canvasLength;
	
var cellHeight = canvasLength/9; // Longitud de una celda

for(i=1;i<=8;i++){ 
  //Pintado de líneas horizontales
  ctx.beginPath();
  ctx.moveTo(0, cellHeight*i);
  if(i%3==0){
    ctx.lineWidth = 4;
  }else{
    ctx.lineWidth = 1;
  }						
  ctx.lineTo(canvasLength, cellHeight*i);
  ctx.stroke();	

  //Pintado de líneas verticales
  ctx.beginPath();
  ctx.moveTo(cellHeight*i, 0);
  ctx.lineTo(cellHeight*i, canvasLength);
  ctx.lineWidth = 1;
  if(i%3==0){
    ctx.lineWidth = 4;
  }else{
    ctx.lineWidth = 1;
  }				
  ctx.stroke();
}

var sudoku = Array();
sudoku = loader(sudoku);

var difficulty = 'veryeasy'; //veryeasy, easy, normal, hard, veryhard, complex
var defValues = getValidCells(difficulty);
var fontsize = .5+(canvasLength*2)/1000;

for(ri=1;ri<10;ri++){    
  for(ci=0;ci<9;ci++){ 					
    // intruce los números
    ctx.fillStyle = "#000000";
    ctx.font = fontsize+"rem Arial";
    ctx.fillText(sudoku[ri][ci], (ci*cellHeight)+cellHeight/3, (ri*cellHeight)-(cellHeight/3)+2);

    // casillas vacias
    ctx.fillStyle = "#FFFFFF";					
    if(inArray(ri+''+ci,defValues)){
      ctx.fillRect(ci*cellHeight+4, (ri*cellHeight+4)-cellHeight, cellHeight-8, cellHeight-8);					
    }
  }
}

function loader(sudoku){
  var col1=Array(),col2=Array(),col3=Array(),col4=Array(),            
    col5=Array(),col6=Array(),col7=Array(),col8=Array(),            
    col9=Array(),grp1=Array(),grp2=Array(),grp3=Array(),   
    grp4=Array(),grp5=Array(),grp6=Array(),grp7=Array(),            
    grp8=Array(),grp9=Array(),row = Array();
  var n,numbers;

  for(ri=1;ri<10;ri++){    
    row = Array();            
    for(ci=1;ci<10;ci++){   			
      switch(ci){
        case 1:	
          if(ri<=3){
            numbers = row.concat(col1,grp1);
            n = getNumber(numbers.filter(onlyUnique));							
            grp1.push(n);
          }else if(ri<=6){
            numbers = row.concat(col1,grp4);
            n = getNumber(numbers.filter(onlyUnique));															
            grp4.push(n);
          }else if(ri<=9){
            numbers = row.concat(col1,grp7);
            n = getNumber(numbers.filter(onlyUnique));							
           grp7.push(n);
          }							
          col1.push(n);
        break;
        case 2:														
          if(ri<=3){
            numbers = row.concat(col2,grp1);
            n = getNumber(numbers.filter(onlyUnique));							
            grp1.push(n);
          }else if(ri<=6){
            numbers = row.concat(col2,grp4);
            n = getNumber(numbers.filter(onlyUnique));
            grp4.push(n);
          }else if(ri<=9){
            numbers = row.concat(col2,grp7);
            n = getNumber(numbers.filter(onlyUnique));
            grp7.push(n);
          }							
          col2.push(n);
        break;
        case 3:													
          if(ri<=3){
            numbers = row.concat(col3,grp1);
            n = getNumber(numbers.filter(onlyUnique));							
            grp1.push(n);
          }else if(ri<=6){
            numbers = row.concat(col3,grp4);
            n = getNumber(numbers.filter(onlyUnique));							
            grp4.push(n);
          }else if(ri<=9){
            numbers = row.concat(col3,grp7);
            n = getNumber(numbers.filter(onlyUnique));							
            grp7.push(n);
          }							
          col3.push(n);
        break;
        case 4:														
          if(ri<=3){
            numbers = row.concat(col4,grp2);
            n = getNumber(numbers.filter(onlyUnique));							
            grp2.push(n);
          }else if(ri<=6){
            numbers = row.concat(col4,grp5);
            n = getNumber(numbers.filter(onlyUnique));							
            grp5.push(n);
          }else if(ri<=9){
            numbers = row.concat(col4,grp8);
            n = getNumber(numbers.filter(onlyUnique));							
            grp8.push(n);
          }							
          col4.push(n);
        break;
        case 5:														
          if(ri<=3){
            numbers = row.concat(col5,grp2);
            n = getNumber(numbers.filter(onlyUnique));							
            grp2.push(n);
          }else if(ri<=6){
            numbers = row.concat(col5,grp5);
            n = getNumber(numbers.filter(onlyUnique));							
            grp5.push(n);
          }else if(ri<=9){
            numbers = row.concat(col5,grp8);
            n = getNumber(numbers.filter(onlyUnique));							
            grp8.push(n);
          }
          col5.push(n);							
        break;
        case 6:														
          if(ri<=3){
            numbers = row.concat(col6,grp2);
            n = getNumber(numbers.filter(onlyUnique));							
            grp2.push(n);
          }else if(ri<=6){
            numbers = row.concat(col6,grp5);
            n = getNumber(numbers.filter(onlyUnique));							
            grp5.push(n);
          }else if(ri<=9){
            numbers = row.concat(col6,grp8);
            n = getNumber(numbers.filter(onlyUnique));							
            grp8.push(n);
          }							
          col6.push(n);
        break;
        case 7:														
          if(ri<=3){
            numbers = row.concat(col7,grp3);
            n = getNumber(numbers.filter(onlyUnique));							
            grp3.push(n);
          }else if(ri<=6){
            numbers = row.concat(col7,grp6);
            n = getNumber(numbers.filter(onlyUnique));							
            grp6.push(n);
          }else if(ri<=9){
            numbers = row.concat(col7,grp9);
            n = getNumber(numbers.filter(onlyUnique));							
            grp9.push(n);
          }							
          col7.push(n);
        break;
        case 8:														
          if(ri<=3){
            numbers = row.concat(col8,grp3);
            n = getNumber(numbers.filter(onlyUnique));							
            grp3.push(n);
          }else if(ri<=6){
            numbers = row.concat(col8,grp6);
            n = getNumber(numbers.filter(onlyUnique));							
            grp6.push(n);
          }else if(ri<=9){
            numbers = row.concat(col8,grp9);
            n = getNumber(numbers.filter(onlyUnique));							
            grp9.push(n);
          }							
          col8.push(n);
        break;	
        case 9:														
          if(ri<=3){
            numbers = row.concat(col9,grp3);
            n = getNumber(numbers.filter(onlyUnique)); 							
            grp3.push(n);
          }else if(ri<=6){
            numbers = row.concat(col9,grp6);
            n = getNumber(numbers.filter(onlyUnique)); 							
            grp6.push(n);
          }else if(ri<=9){
            numbers = row.concat(col9,grp9);
            n = getNumber(numbers.filter(onlyUnique)); 							
            grp9.push(n);
          }							
          col9.push(n);
        break;						
      }				
      if(n){
        row.push(n)
      }else{						
        return loader(sudoku);
      }
    }	
    sudoku[ri] = row;                
  }
  return sudoku;			
}

function onlyUnique(value, index, self) { 
  return self.indexOf(value) === index;
}

function getNumber(data){
  if(data.length>8){
    return 0;
  }

  var num = Math.floor((Math.random() * 9) + 1);
  if(inArray(num,data)){
    return getNumber(data);			
  }else{
    return num;
  }
}	

function inArray(needle,haystack) {
  var length = haystack.length;
  for(var i = 0; i < length; i++) {
    if(haystack[i] == needle) return true;
  }			
  return false;
} 	   
	   
function getValidCells(difficulty){		

  var values = Array();
  for(ri=1;ri<10;ri++){
    for(ci=0;ci<9;ci++){ 					
      values.push(ri+''+ci);
    }
  }			

  values = shuffle(values);

  var difficultyValue;
  switch(difficulty){
    case 'veryeasy':
      difficultyValue = 70;
    break;
    case 'easy':
      difficultyValue = 60;
    break;
    case 'normal':
      difficultyValue = 50;
    break;
    case 'hard':
      difficultyValue = 40;
    break;
    case 'veryhard':
      difficultyValue = 30;
    break;				
    case 'complex':
      difficultyValue = 17;
    break;				
  }

  for(i=0;i<difficultyValue;i++){
    values.shift();
  }
  return values;
}		

function shuffle(a) {
  var j, x, i;
  for (i = a.length - 1; i > 0; i--) {
    j = Math.floor(Math.random() * (i + 1));
    x = a[i];
    a[i] = a[j];
    a[j] = x;
  }
  return a;
}		   
	   
</script>
</body>
</html>

 

Comentarios 1 comentario

Comentario

Smith

Amazing!