Estoy tratando de crear un juego de mesa con p5.js (Javascript)
Para configurar el tablero de juego, que es una cuadrícula de 6 por 6, tengo que llenar la cuadrícula con 6 colores de manera que ninguna celda horizontal o vertical que se toque tenga el mismo color. Y los 6 colores deben usarse en 6 celdas.
Pero ahora estoy luchando un poco para crear un algoritmo que coloque los colores al azar pero manteniendo las reglas.
Traté de comenzar en la esquina superior izquierda, rellenando con un color aleatorio. Luego empiezo a llenar la celda de la izquierda y la parte inferior con un color diferente.
El problema es que cuando el script quiere llenar las últimas celdas, no quedan colores para usar (o ya se llenaron 6 celdas o un color restante es un vecino)
Ejemplo: aún deben ser rojas dos celdas, pero solo queda un lugar para el rojo (debajo del blanco):
//fill placedColors Array with zeros placedColors = []; for(let i=0; i<6; i++) { placedColors[i] = 0; } //fill allIndexes Array with indizies to keep control of visited cells let allIndexes = []; for(let i=0; i<36; i++) { allIndexes.push(i); } //build board //when I set the limit to 36 the script runs forever because no solution is found for(let i=0; i<33; i++) { fillCells(i); } function fillCells(index) { //get top and left color let topColor = false; //index is in the second row if(index >= 6) { topColor = cells[index-6].color; } let leftColor = false; //index is not in the first column if(index % 6 > 0 && index > 0) { leftColor = cells[index-1].color; } if(allIndexes.indexOf(index) > -1) { cells.push(new Cell(index, pickColor(topColor, leftColor))); } //mark index as visited var allIndexesIndex = allIndexes.indexOf(index); if (allIndexesIndex !== -1) { allIndexes.splice(allIndexesIndex, 1); } } function pickColor(invalidColor1,invalidColor2) { let colorFound = false; do { randColor = floor(random(6)); if(placedColors[randColor] < 6 && randColor!=invalidColor1 && randColor!=invalidColor2) { placedColors[randColor]++; colorFound = true; } } while(!colorFound); return randColor; }
¡Gracias por tus sugerencias! Traté de encontrar una solución propia paralela a la publicada. Ahora con este código, funciona bien:
function buildBoard() { cells = []; for(let i=0; i<gameSize; i++) { placedColors[i] = 0; } for(var i=0; i<gameSize*gameSize; i++) { cells.push(new Cell(i, pickColor())); } do { invalidFields = []; findInvalidFields(); if(invalidFields.length > 0) { let cell1index = Math.floor(Math.random() * invalidFields.length); cell1 = invalidFields[cell1index]; //check, if cell in different color available let otherColorAvailable = false; for(cell of invalidFields) { if(cell.color != cell1.color) { otherColorAvailable = true; break; } } if(otherColorAvailable) { //pick invalid cell do { let cell2index = Math.floor(Math.random() * invalidFields.length); cell2 = invalidFields[cell2index]; } while (cell2.color == cell1.color) } else { //pick random cell do { let cell2index = Math.floor(Math.random() * cells.length); cell2 = cells[cell2index]; } while (cell2.color == cell1.color) } //switch colors of cells let tempColor = cell1.color; cell1.color = cell2.color; cell2.color = tempColor; } } while (!checkStartField()); } function findInvalidFields() { for(let index=0; index<cells.length; index++) { let thisColor = cells[index].color; //right if(index%gameSize < gameSize-1 && cells[index+1].color == thisColor) { if(invalidFields.indexOf(cells[index+1])) { invalidFields.push(cells[index+1]); } } //bottom if(index < gameSize*gameSize-gameSize && cells[index+gameSize].color == thisColor) { if(invalidFields.indexOf(cells[index+gameSize])) { invalidFields.push(cells[index+gameSize]); } } } } function checkStartField() { for(let index=0; index<cells.length; index++) { let thisColor = cells[index].color; //top if(index >= gameSize && cells[index-gameSize].color == thisColor) { //console.log(index+'top'); return false; } //right if(index%gameSize < gameSize-1 && cells[index+1].color == thisColor) { //console.log(index+'right'); return false; } //bottom if(index < gameSize*gameSize-gameSize && cells[index+gameSize].color == thisColor) { //console.log(index+'bottom'); return false; } //left if(index%gameSize > 0 && cells[index-1].color == thisColor) { //console.log(index+'left'); return false; } } return true; }
Un enfoque fácil es comenzar con un coloreado válido (por ejemplo, cualquier cuadrado latino de 6x6 es un coloreado válido) y luego mezclarlo encontrando un par de cosas que se puedan intercambiar e intercambiarlas.
Una mejora (para aumentar la velocidad de mezcla y para evitar que el algoritmo se atasque) es permitir que, como máximo, una celda sea inválida (es decir, una celda que, si se elimina, deja el resto con un color válido). Si no hay una celda no válida, intercambie dos celdas aleatorias si al menos una de ellas será válida después del intercambio. Y si hay una celda no válida, elija esa celda y otra celda aleatoria para intercambiar, suponiendo que el intercambio deje al menos una de ellas válida. Nuevamente repita mucho tiempo, deteniéndose solo cuando el coloreado sea válido.
Una implementación de esta idea (lo siento, no Javascript) está aquí: https://go.dev/play/p/sxMvLxHfhmC