Ayuda con iteración en bucle for

AikonCWD

Estoy delante de un problema pero por más que lo reviso, no veo el fallo xd. Sé que es una tontería, pero a ver si alguien lo ve rápido y me ayuda.

Tengo el siguiente escenario:

Esto es una matriz de 2 dimensiones matrix[x][y]

Y tras ejecutar el siguiente código:

for x in range(51):
	for y in range(51):
		if matrix[x][y] == 0:
			var t = 0
			for d in directions:
				t += count_cells(x,y,d)
				if t == 3:
					matrix[x][y] = 1

Obtengo este resultado NO deseado:


Lo que tendría que hacer ese código es buscar una pared que tenga 3 espacios libres a su alrededor, y borrar esa pared.
Si nos fijamos en la foto final, en alguna de esas paredes hace lo esperado (es decir, se borra), pero en alguna otras lo que hace es borrar la pared y las siguientes (marcado en rojo).

Creo que tengo que salir del bucle con un continue o un break en algún momento. Pero no doy con la tecla correcta. Alguien lo ve y me ayuda? Gracias

edit:

matrix[5][5] = 0 esto sería una pared en la posición 5,5
matrix[5][5] = 1 esto sería un hueco vacío en la posición 5,5
count_cells() devuelve el valor de la celda de al lado, según la dirección d


edit 2:

El fallo lo veo claro. El bucle funciona bien, borra la pared, pero a la que avanza la x y la y, encuentra una nueva pared con t==3, fruto del ciclo anterior.
Por eso me borra tantas paredes de forma consecutiva.
Como evito este efecto? Que haga los cálculos sobre una copia de la matriz sin modificar?

carra

Tú mismo has visto el problema, y la posible solución. Esto te pasará en los extremos de paredes que terminan apuntando hacia arriba y hacia la izquierda, hay también otros lugares de ese laberinto donde pasa lo mismo.

Otra posible solución, en vez de copiar la matriz cada iteración, sería que en cada casilla tengas un booleano adicional que marca la casilla para borrar. Así los siguientes ciclos del bucle aún la ven. Luego harías una segunda pasada para borrar todas las que estén marcadas.

1 respuesta
Czhincksx

No sé si escribiré el código bien porque no sé qué lenguaje es ni nada, pero yo haría esto:

for x in range(51):
	for y in range(51):
		if matrix[x][y] == 0 && auxMatrix[x][y] == 0:
			var t = 0
			for d in directions:
				t += count_cells(x,y,d)
				if t == 3:
					matrix[x][y] = 1
					auxMatrix[x?][y?] = 1 
//el valor de x e y depende de d que no sé cómo va

o bien usar una matriz de enteros en vez de booleanos y tener un código para muros que no quieres borrar, como el 2.

1 respuesta
AikonCWD

#3 No entiendo tu código.

directions es un array con valores: N, S, E y O. Lo que hago en ese bucle interno es a partir de una posición X,Y, mirar el valor de las casillas Norte, Sud, Este y Oeste. Y recojo su valor. Si suman 3 espacios vacíos es que estamos ante una pared que se debe borrar.

1 respuesta
Czhincksx

#4 A lo mejor no he entendido bien lo que pretendes. Lo que yo había entendido es que tras aplicar el algoritmo, querías que este borrara el último ladrillo de cada espigón, que es el que tiene 3 espacios libres alrededor, y que no siguiera borrando más de ese muro en concreto. ¿No era eso?

1 respuesta
AikonCWD

#5 Exacto. El efecto que estoy obteniendo es lo que dice #2

auxMatrix que es? Una copia de la matriz inicial? De ser así, por qué la editas en el último if?

1 respuesta
B

animo #6 un cafe y lo tienes

1
eondev
#1AikonCWD:

Como evito este efecto? Que haga los cálculos sobre una copia de la matriz sin modificar?

Sí. Recorres una matriz, aplicas en otra

1
AikonCWD

Vale, pues o soy subnormal o esto me está troleando:

Así lo tengo ahora y obtengo el mismo resultado. Mención especial que a la función de count_cells, le paso la copia de la matriz inicial. Y me sigue borrando cachos de pared enteros.

2 respuestas
eondev

#9 Eso es porque estás apuntando a la misma lista. Haz un tmp_matrix = matrix.copy()

1 1 respuesta
AikonCWD

#10 VAAAALE coño. No sabía que esa asignación era la misma lista. Vaya faena.
Lo acabo de ver en la documentación:

copy() no existe, es duplicate() y he tenido que meter el deep = true al ser un array de arrays (bidimensional). Ahora ya funciona, joder.

Muchas gracias chicos.

1 respuesta
eondev

#11 Ah coño, que no es Python vale xDDD

1 respuesta
Hipnos

¿Por qué no calculas primero las celdas a eliminar y luego las eliminas?

1 respuesta
Czhincksx

#9 Primero mira a ver si en ese lenguaje las asignación de matrices copia o referencia. También tienes que sacar el if t == 3 del bucle porque si no te va a borrar siempre que algo esté rodeado por 4 paredes y hayas pasado por 3 coordenadas.

for x in range(51):
	for y in range(51):
		if matrix[x][y] == 0 && auxMatrix[x][y] == 0:
			var t = 0
			var q = 0
			for d in directions:
				if count_cells(x,y,d) == 1
					t += 1
				else q = d
			if t == 3:
				var (x1,y1) = getCoor(x, y, q)
				matrix[x][y] = 1
				auxMatrix[x1][y1] = 1 


// Una función así para saber qué celda quieres marcar como sucia la necesitarás
Vector2 (x,y) = getCoor(x,y,d)

Esta sería una posible solución.

Otra más sencilla sería:

for x in range(51):
	for y in range(51):
		if matrix[x][y] == 2:
			var t = 0
			for d in directions:
				t += count_cells(x,y,d)
			if t == 3:
				matrix[x][y] = 2

for x in range(51):
	for y in range(51):
		if matrix[x][y] == 2:
			matrix[x][y] = 1

// En este otro caso tendrías que asegurarte de que count_cells tiene en cuenta que 2 se considera también muro.
1 respuesta
AikonCWD

#13 Para evitar recorrer el array 2 veces. Imagina que el mapa en lugar de ser de 50x50, termina siendo de 500.000x500.000.
Y también por cabezonería xd.

#12 GDScript, un fork de python para Godot. Es prácticamente igual, salvo algunos detalles como este xd.

Así me ha quedado la función, funciona de coña :P

#14 Al final lo he solventado así:

Recorriendo una copia del array sin modificar. Lo que no sabía es que estaba creando una referencia a la misma lista en lugar de duplicar el array. @eondev me dio la solución

1 1 respuesta
X-Crim

Grande @eondev nuestro programador junior 🤘

4
Hipnos

#15 Me refiero a algo así:

for x in range(51):
	for y in range(51):
		if matrix[x][y] == 0:
			var t = 0
			for d in directions:
				t += count_cells(x,y,d)
				if t == 3:
					delete[j] = {[x],[y]}
                                        j+=1

//Luego recorres solo delete[j] y no la matriz entera
1 respuesta
AikonCWD

#17 Pues sería otra solución muy válida, la verdad... Crear un a matriz j con las celdas a borrar. Muy bien pensado.

1 respuesta
Hipnos

#18 Con un array de coordenadas es suficiente, no es necesario otra matriz del mismo tamaño.

Te haces una función sencilla que vaya a punto [x,y] y lo sustituya por 1.

1 respuesta
X-Crim

Grande hipnos

AikonCWD

#19 Sí sí, funcionaría igual de bien la verdad. Pero bueno, ya lo dejo así implementado que es como lo tenía imaginado originalmente en mi cabeza.

Por cierto, estoy haciendo un roguelike. Tradicional. Ya os lo iré enseñando por el hilo
Estoy liado con la generación de mazmorras aleatorias.

1 respuesta
Hipnos

#21 Ya lo sé, por eso estoy aquí. Porque huelo lo que quieres hacer. Soy capaz de sentir la esencia de un roguelike desde su gestación.

3
eXtreM3

Si hay algo en esta vida que jamás hubiera sido capaz de imaginar, esa es sin duda ver a @eondev con @Hipnos resolviendo un problema de programación de @AikonCWD

2 respuestas
laZAr0

Iba a darte yo la solución, una lastima que llegué tarde.

Hipnos

#23 Aquí donde me ves tengo tres títulos de ingeniería y medio doctorado en informática xd

A mí programar me gusta pero me aburro a los 10 minutos, no tengo constancia. En su día estuve ojeando para hacer un roguelike, pero nada, proyecto abandonado nº 351.

Al que se anime a hacerlo y tenga constancia, le recomiendo que se lea la biblia que recomiendan los tipos que programaron el Caves of Qud: https://gameprogrammingpatterns.com/

1 respuesta
AikonCWD

#23 eondev es experto programando en python. Aunque esta duda era inherente al lenguaje

B

Solucionado lo que tenías... como consejo (más que nada por que no lo veo) ... no solo te guardes un array de (pintado, no pintado). Si usas una variable de apoyo para saber cuantos pasos tienes que avanzar para alcanzar la siguiente baldosa que necesita ser dibujada te ahorras muchas iteraciones.
Para esta "técnica" lo más sencillo es usar arrays unidimensionales en vez de bidimensionales.

1 respuesta
desu

#25 Yo me lei el libro, esta bastante bien, alguna cosa obsoleta pero el 90% es buen contenido. Aunque esta mas orientada a "software industrial" o "proyectos grandes".

B

.

AikonCWD

Bueno, la cosa va tomando forma poco a poco. Me falta conectar cada habitación roja con el laberinto antes de "limpiarlo".

1 1 respuesta