Python real no tiene un concepto equivalente — esto es una pieza propia de BloquePy.
Es el terreno ideal para todo lo que se piensa como una cuadrícula grande de celdas en vez de un puñado de
agentes: un mapa de calor, un autómata celular, un rastro de feromona que se desvanece. Las tortugas y
Patches conviven en el mismo lienzo — los agentes se mueven sobre el ambiente, y el ambiente
puede reaccionar a ellos.
Crear la cuadrícula
Filas × columnas — una cuadrícula de celdas de color, independiente de las tortugas. Un 3er argumento opcional decide qué pasa en el borde: "wrap" (de siempre, el mundo se envuelve) o "wall" — una celda de borde tiene MENOS vecinas reales en vez de envolver, para cuando envolver no tiene sentido físico (un incendio no debería reaparecer del otro lado del mapa).
mundo = Patches(20, 20)
mundo = Patches(20, 20, "wall")
Leer / escribir una celda
Como una lista de listas — el segundo índice es obligatorio (indexar solo la fila da un objeto intermedio, no un color).
mundo[fila][columna] = "red"
mundo[fila][columna] = (0, 255, 0)
print(mundo[fila][columna])
Las 8 vecinas (con diagonales)
Devuelve una lista de 8 colores (r, g, b), uno por cada celda vecina — el mundo se envuelve en los bordes (la última fila es vecina de la primera).
mundo.neighbors(fila, columna)
Las 4 vecinas (sin diagonales)
Igual que neighbors, pero solo arriba/abajo/izquierda/derecha, EN ESE ORDEN — las diagonales no cuentan. En modo "wall", si una de las 4 no existe (estás en el borde) se omite de la lista en vez de inventarla.
mundo.neighbors4(fila, columna)
Difundir el color
Un paso de difusión: cada celda se mezcla un poco con el promedio de color de sus 8 vecinas (0 = nada cambia, 1 = queda exacto en el promedio). Corre de una sola vez sobre TODA la cuadrícula, mucho más rápido que hacerlo celda por celda a mano.
mundo.diffuse(0.3)
Además del color, cada celda guarda por separado un número — pensado para una cantidad real
(temperatura, concentración, nivel de agua) que necesitás para hacer cuentas, sin que pintar la celda de otro
color te lo borre. Mismas cuatro operaciones que el color, pero sobre ese número:
Leer el número de una celda
El número guardado en esa celda, independiente de su color — empieza en 0 si nunca se escribió.
mundo.value(fila, columna)
Escribir el número de una celda
A diferencia del color, el número no se escribe con = — es su propio bloque, con la fila, la columna y el valor nuevo.
mundo.setvalue(fila, columna, 8.5)
Números de las vecinas (8 y 4)
Como neighbors/neighbors4, pero devuelve una lista de NÚMEROS en vez de colores, en el mismo orden fijo (arriba, abajo, izquierda, derecha para la de 4) — el orden es justo lo que hace falta para calcular un gradiente a mano (derecha - izquierda). Para eso ya existe mundo.gradient(...) más abajo, que lo hace por vos.
mundo.neighborvalues(fila, columna)
mundo.neighborvalues4(fila, columna)
Difundir el número
Lo mismo que diffuse, pero mezclando el NÚMERO de cada celda con el de sus vecinas — así es como se simula una difusión real (calor, una sustancia) sin que el color quede de por medio.
mundo.diffusevalue(0.3)
Ninguno de los dos canales (color ni número) "evapora" solo — si querés que un rastro se desvanezca con el
tiempo (como una feromona), hay que bajarlo a mano, celda por celda, antes de difundir (ver el ejemplo de
hormigas más abajo).
diffuse/diffusevalue mezclan TODA la cuadrícula parejo — pero a veces una parte del
terreno tiene que quedarse quieta y bloquear el paso, como una roca en medio de un río o una pared en un
incendio. Para eso existen las celdas "impermeables": se marcan una vez, y a partir de ahí se
comportan como una pared real — ni cambian ellas, ni dejan que sus vecinas "las vean" al promediar.
Marcar (o desmarcar) una celda
A partir de ahora, diffuse/diffusevalue ignoran por completo esta celda — no la cambian, y las celdas vecinas dejan de contarla en su propio promedio, como si no existiera. False la vuelve a dejar normal.
mundo.setsealed(fila, columna, True)
mundo.setsealed(fila, columna, False)
¿Está sellada?
False para cualquier celda que nunca se haya tocado con setsealed — útil dentro de un if antes de decidir algo sobre esa celda (ej. no dejar que un agente camine por ahí).
mundo.sealed(fila, columna)
A diferencia de pintar la celda de un color "de pared", sellarla no cambia cómo se ve — solo cómo se
comporta ante la difusión. Si además querés que se vea distinta, pintala vos con
mundo[fila][columna] = ... por separado.
Y para conectar los dos mundos — una tortuga que necesita saber sobre qué celda está parada, sin convertir
xcor()/ycor() a fila/columna a mano cada vez:
¿Sobre qué celda está parada la tortuga?
Convierte la posición ACTUAL de la tortuga (xcor()/ycor()) en la fila y columna de mundo que le corresponden, sin hacer esa cuenta a mano. Se le pasa la cuadrícula porque puede haber más de una a la vez.
f, c = t1.patch(mundo)
mundo[f][c] = "red"
Y para no tener que inventar tu propia fórmula de "número a color" cada vez que armás un mapa de calor:
Número → color (frío a cálido)
Convierte un número a un color azul→blanco→rojo, recortado al rango que le des — reemplaza el min([255, ...])/max([...]) a mano que hacía falta antes para pintar un mapa de calor.
mundo[f][c] = mundo.heatcolor(mundo.value(f, c), 0, 100)
Leyenda de color
Dibuja una franja de color con etiqueta en la esquina del lienzo, explicando qué significan los colores — mismo colormap y rango que heatcolor(...), así que nunca se desincroniza de lo que de verdad se pintó.
mundo.legend("temperatura", 0, 100)
Y para diagramas de campo vectorial de verdad — flechas que apuntan hacia donde un número crece más rápido:
Dirección de mayor crecimiento (gradiente)
El ángulo (mismo sistema que heading()) hacia donde el número guardado con setvalue(...) crece más rápido en esa celda — calculado con diferencias finitas sobre las 4 vecinas ortogonales. Combinalo con la forma "flecha" (la única que rota y no es un ícono de animal) para armar un diagrama de campo vectorial: una tortuga quieta por celda, cada una apuntando a su propio gradiente.
f = Turtle()
f.shape("flecha")
f.setheading(mundo.gradient(fila, columna))