Laberinto — explorar sin conocer el mapa (nivel 3)
El algoritmo más avanzado de la galería: explora el laberinto SIN conocer el mapa, con DFS real (dos vectores paralelos de posiciones visitadas + una pila de direcciones para retroceder cuando choca con un callejón sin salida). Combina vectores, ciclos, condiciones, Según y llamadas de sistema en un solo lugar. Este ejemplo carga un laberinto CON solución — ver también "sin solución" para la otra mitad del ejercicio.
Abrir en el editor →Algoritmo de ejemplo, listo para ejecutar paso a paso en el navegador. Sin instalar nada.
Diagrama de flujo
Pseudocódigo
// Nivel 3 de Karel: explora el laberinto SIN conocer el mapa, usando DFS con memoria de posiciones visitadas (dos vectores paralelos) y una pila de direcciones para retroceder (backtracking) cuando se topa con un callejón sin salida. Si agota todo lo alcanzable sin encontrar la salida, concluye con certeza que es imposible — no es solo un heurístico que se rinde, es una exploración exhaustiva real.
Algoritmo main
Activar("Laberinto")
CargarEscenario("default")
fila <- 1
columna <- 1
n <- 1
visFila(1) <- fila
visColumna(1) <- columna
profundidad <- 0
encontrado <- 0
imposible <- 0
Mientras NO encontrado Y NO imposible
Llegue()
Si response_call
encontrado <- 1
SiNo
BuscarDireccion(fila, columna, visFila, visColumna, n, direccionElegida, hayDireccion)
Si hayDireccion
MOVER(direccionElegida, 1)
Delta(direccionElegida, fila, columna, fila, columna)
n <- n+1
visFila(n) <- fila
visColumna(n) <- columna
profundidad <- profundidad+1
pilaDir(profundidad) <- direccionElegida
SiNo
Si profundidad==0
imposible <- 1
SiNo
direccionTomada <- pilaDir(profundidad)
profundidad <- profundidad-1
Opuesta(direccionTomada, direccionVolver)
MOVER(direccionVolver, 1)
Delta(direccionVolver, fila, columna, fila, columna)
FinSi
FinSi
FinSi
FinMientras
Si encontrado
Escribir "Encontré la salida — exploré ", n, " posiciones."
SiNo
Escribir "Es IMPOSIBLE llegar a la salida — exploré todo lo alcanzable (", n, " posiciones) sin encontrarla."
FinSi
FinAlgoritmo
Funcion BuscarDireccion(filaAct, columnaAct, visFila, visColumna, n, direccionElegida, hayDireccion)
orden(1) <- "ARRIBA"
orden(2) <- "DERECHA"
orden(3) <- "ABAJO"
orden(4) <- "IZQUIERDA"
hayDireccion <- 0
Para i <- 1 Hasta 4
Si NO hayDireccion
d <- orden(i)
HayObstaculo(d)
Si NO response_call
Delta(d, filaAct, columnaAct, filaC, columnaC)
EstaVisitado(filaC, columnaC, visFila, visColumna, n, visitado)
Si NO visitado
direccionElegida <- d
hayDireccion <- 1
FinSi
FinSi
FinSi
FinPara
FinFuncion
Funcion Delta(direccion, filaAct, columnaAct, filaC, columnaC)
Segun direccion
Caso ARRIBA:
filaC <- filaAct-1
columnaC <- columnaAct
Caso ABAJO:
filaC <- filaAct+1
columnaC <- columnaAct
Caso IZQUIERDA:
filaC <- filaAct
columnaC <- columnaAct-1
Caso DERECHA:
filaC <- filaAct
columnaC <- columnaAct+1
De Otro Modo:
FinSegun
FinFuncion
Funcion EstaVisitado(filaC, columnaC, visFila, visColumna, n, resultado)
resultado <- 0
Para i <- 1 Hasta n
Si NO resultado
Si visFila(i)==filaC Y visColumna(i)==columnaC
resultado <- 1
FinSi
FinSi
FinPara
FinFuncion
Funcion Opuesta(direccion, resultado)
Segun direccion
Caso ARRIBA:
resultado <- "ABAJO"
Caso ABAJO:
resultado <- "ARRIBA"
Caso IZQUIERDA:
resultado <- "DERECHA"
Caso DERECHA:
resultado <- "IZQUIERDA"
De Otro Modo:
FinSegun
FinFuncion
El mismo algoritmo en Java
package org.desarrollo;
import java.util.ArrayList;
import java.util.Scanner;
/**
* Nivel 3 de Karel: explora el laberinto SIN conocer el mapa, usando DFS con memoria de posiciones visitadas (dos vectores paralelos) y una pila de direcciones para retroceder (backtracking) cuando se topa con un callejón sin salida. Si agota todo lo alcanzable sin encontrar la salida, concluye con certeza que es imposible — no es solo un heurístico que se rinde, es una exploración exhaustiva real.
*/
public class FreeDFDMain {
/**
* Punto de entrada del programa generado por freeDFDWeb.
*
* @param args no utilizado — freeDFDWeb no pasa argumentos de línea de comandos.
*/
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
double[] fila = {0.0}; // Arreglo de 1 elemento: simula paso por referencia (Java no permite mutar un double pasado a otro método) — se usa como "fila[0]" en el resto del método
double[] columna = {0.0}; // Arreglo de 1 elemento: simula paso por referencia (Java no permite mutar un double pasado a otro método) — se usa como "columna[0]" en el resto del método
double n = 0.0;
ArrayList<Double> visFila = new ArrayList<>();
ArrayList<Double> visColumna = new ArrayList<>();
double profundidad = 0.0;
double encontrado = 0.0;
double imposible = 0.0;
double response_call = 0.0;
double[] direccionElegida = {0.0}; // Arreglo de 1 elemento: simula paso por referencia (Java no permite mutar un double pasado a otro método) — se usa como "direccionElegida[0]" en el resto del método
double[] hayDireccion = {0.0}; // Arreglo de 1 elemento: simula paso por referencia (Java no permite mutar un double pasado a otro método) — se usa como "hayDireccion[0]" en el resto del método
ArrayList<Double> pilaDir = new ArrayList<>();
double direccionTomada = 0.0;
double[] direccionVolver = {0.0}; // Arreglo de 1 elemento: simula paso por referencia (Java no permite mutar un double pasado a otro método) — se usa como "direccionVolver[0]" en el resto del método
System.out.println("Activar(" + "Laberinto" + ")"); // Componente interactivo no disponible en el código exportado
System.out.println("CargarEscenario(" + "default" + ")"); // Componente interactivo no disponible en el código exportado
fila[0] = 1;
columna[0] = 1;
n = 1;
vecSet(visFila, (int)(1), fila[0]);
vecSet(visColumna, (int)(1), columna[0]);
profundidad = 0;
encontrado = 0;
imposible = 0;
while ((((!((encontrado) != 0)) && (!((imposible) != 0))))) {
System.out.println("Llegue(" + "" + ")"); // Componente interactivo no disponible en el código exportado
if (((response_call) != 0)) {
encontrado = 1;
} else {
BuscarDireccion(fila[0], columna[0], visFila, visColumna, n, direccionElegida, hayDireccion);
if (((hayDireccion[0]) != 0)) {
System.out.println("MOVER(" + fmt(direccionElegida[0]) + ", " + fmt(1) + ")"); // Componente interactivo no disponible en el código exportado
Delta(direccionElegida[0], fila[0], columna[0], fila, columna);
n = (n + 1);
vecSet(visFila, (int)(n), fila[0]);
vecSet(visColumna, (int)(n), columna[0]);
profundidad = (profundidad + 1);
vecSet(pilaDir, (int)(profundidad), direccionElegida[0]);
} else {
if (((profundidad == 0))) {
imposible = 1;
} else {
direccionTomada = vecGet(pilaDir, (int)(profundidad));
profundidad = (profundidad - 1);
Opuesta(direccionTomada, direccionVolver);
System.out.println("MOVER(" + fmt(direccionVolver[0]) + ", " + fmt(1) + ")"); // Componente interactivo no disponible en el código exportado
Delta(direccionVolver[0], fila[0], columna[0], fila, columna);
}
}
}
}
if (((encontrado) != 0)) {
System.out.println("Encontré la salida — exploré " + fmt(n) + " posiciones.");
} else {
System.out.println("Es IMPOSIBLE llegar a la salida — exploré todo lo alcanzable (" + fmt(n) + " posiciones) sin encontrarla.");
}
}
/**
* Subprograma generado por freeDFDWeb, sin descripción en el diagrama original.
*
* @param filaAct double
* @param columnaAct double
* @param visFila double
* @param visColumna double
* @param n double
* @param direccionElegida double[] — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
* @param hayDireccion double[] — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
*/
static void BuscarDireccion(double filaAct, double columnaAct, double visFila, double visColumna, double n, double[] direccionElegida, double[] hayDireccion) {
ArrayList<Double> orden = new ArrayList<>();
double i = 0.0;
double d = 0.0;
double response_call = 0.0;
double[] filaC = {0.0}; // Arreglo de 1 elemento: simula paso por referencia (Java no permite mutar un double pasado a otro método) — se usa como "filaC[0]" en el resto del método
double[] columnaC = {0.0}; // Arreglo de 1 elemento: simula paso por referencia (Java no permite mutar un double pasado a otro método) — se usa como "columnaC[0]" en el resto del método
double[] visitado = {0.0}; // Arreglo de 1 elemento: simula paso por referencia (Java no permite mutar un double pasado a otro método) — se usa como "visitado[0]" en el resto del método
vecSet(orden, (int)(1), "ARRIBA");
vecSet(orden, (int)(2), "DERECHA");
vecSet(orden, (int)(3), "ABAJO");
vecSet(orden, (int)(4), "IZQUIERDA");
hayDireccion[0] = 0;
double _forStart1 = 1;
double _forEnd1 = 4;
double _forStep1 = 1;
if (_forStep1 == 0) { throw new RuntimeException("El incremento de \"para i\" no puede ser 0"); }
for (i = _forStart1; _forStep1 > 0 ? i <= _forEnd1 : i >= _forEnd1; i += _forStep1) {
if ((!((hayDireccion[0]) != 0))) {
d = vecGet(orden, (int)(i));
System.out.println("HayObstaculo(" + fmt(d) + ")"); // Componente interactivo no disponible en el código exportado
if ((!((response_call) != 0))) {
Delta(d, filaAct, columnaAct, filaC, columnaC);
EstaVisitado(filaC[0], columnaC[0], visFila, visColumna, n, visitado);
if ((!((visitado[0]) != 0))) {
direccionElegida[0] = d;
hayDireccion[0] = 1;
}
}
}
}
}
/**
* Subprograma generado por freeDFDWeb, sin descripción en el diagrama original.
*
* @param direccion double
* @param filaAct double
* @param columnaAct double
* @param filaC double[] — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
* @param columnaC double[] — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
*/
static void Delta(double direccion, double filaAct, double columnaAct, double[] filaC, double[] columnaC) {
double _switch2 = direccion;
if (_switch2 == 0.0) {
filaC[0] = (filaAct - 1);
columnaC[0] = columnaAct;
} else if (_switch2 == 0.0) {
filaC[0] = (filaAct + 1);
columnaC[0] = columnaAct;
} else if (_switch2 == 0.0) {
filaC[0] = filaAct;
columnaC[0] = (columnaAct - 1);
} else if (_switch2 == 0.0) {
filaC[0] = filaAct;
columnaC[0] = (columnaAct + 1);
} else {
}
}
/**
* Subprograma generado por freeDFDWeb, sin descripción en el diagrama original.
*
* @param filaC double
* @param columnaC double
* @param visFila ArrayList<Double>
* @param visColumna ArrayList<Double>
* @param n double
* @param resultado double[] — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
*/
static void EstaVisitado(double filaC, double columnaC, ArrayList<Double> visFila, ArrayList<Double> visColumna, double n, double[] resultado) {
double i = 0.0;
resultado[0] = 0;
double _forStart3 = 1;
double _forEnd3 = n;
double _forStep3 = 1;
if (_forStep3 == 0) { throw new RuntimeException("El incremento de \"para i\" no puede ser 0"); }
for (i = _forStart3; _forStep3 > 0 ? i <= _forEnd3 : i >= _forEnd3; i += _forStep3) {
if ((!((resultado[0]) != 0))) {
if (((((vecGet(visFila, (int)(i)) == filaC)) && ((vecGet(visColumna, (int)(i)) == columnaC))))) {
resultado[0] = 1;
}
}
}
}
/**
* Subprograma generado por freeDFDWeb, sin descripción en el diagrama original.
*
* @param direccion double
* @param resultado String[] — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
*/
static void Opuesta(double direccion, String[] resultado) {
double _switch4 = direccion;
if (_switch4 == 0.0) {
resultado[0] = "ABAJO";
} else if (_switch4 == 0.0) {
resultado[0] = "ARRIBA";
} else if (_switch4 == 0.0) {
resultado[0] = "DERECHA";
} else if (_switch4 == 0.0) {
resultado[0] = "IZQUIERDA";
} else {
}
}
static boolean readBoolean(Scanner scanner) {
String t = scanner.nextLine().trim().toLowerCase();
if (t.equals("verdadero") || t.equals("true") || t.equals("1")) return true;
if (t.equals("falso") || t.equals("false") || t.equals("0")) return false;
throw new RuntimeException("Se esperaba Verdadero o Falso, se recibio: " + t);
}
static double readDouble(Scanner scanner) {
return Double.parseDouble(scanner.nextLine().trim());
}
static ArrayList<Double> readVector(Scanner scanner) {
ArrayList<Double> result = new ArrayList<>();
for (String token : scanner.nextLine().split(",")) {
String t = token.trim();
if (!t.isEmpty()) result.add(Double.parseDouble(t));
}
return result;
}
/** Formatea un double sin ".0" final cuando es entero — replica cómo freeDFDWeb (JS) imprime números, ej. factorial(5) debe mostrar "120", no "120.0". */
static String fmt(double d) {
if (!Double.isInfinite(d) && !Double.isNaN(d) && d == Math.floor(d) && Math.abs(d) < 1e15) {
return String.valueOf((long) d);
}
return String.valueOf(d);
}
/** `String.valueOf(true)` da "true" — el pseudolenguaje escribe e imprime "Verdadero"/"Falso". */
static String boolStr(boolean b) {
return b ? "Verdadero" : "Falso";
}
/** `ToChar(n)` de Flowgorithm: código de carácter → carácter. `Character.toChars` cubre también los puntos fuera del BMP (emoji), que no caben en un `char`. */
static String toChar(double code) {
int c = (int) code;
if (c < 0 || c > 0x10FFFF) {
throw new RuntimeException("ToChar(" + c + "): codigo de caracter fuera de rango");
}
return new String(Character.toChars(c));
}
/** `ToFixed(r, i)` de Flowgorithm: TRUNCA a `i` decimales, no redondea. `Locale.ROOT` para que el separador sea el punto siempre, igual que el resto del generador. */
static String toFixed(double value, double digits) {
int d = (int) digits;
double factor = Math.pow(10, d);
double truncated = (double)(long)(value * factor) / factor;
return String.format(java.util.Locale.ROOT, "%." + d + "f", truncated);
}
static double vecGet(ArrayList<Double> v, int index) {
int i = index - 1;
if (i < 0 || i >= v.size() || v.get(i) == null) {
throw new RuntimeException("La posición " + index + " del vector no tiene un valor asignado todavía");
}
return v.get(i);
}
static void vecSet(ArrayList<Double> v, int index, double value) {
int i = index - 1;
while (v.size() <= i) v.add(null);
v.set(i, value);
}
static double div(double a, double b) {
if (b == 0) throw new ArithmeticException("División por cero");
return a / b;
}
static double mod(double a, double b) {
if (b == 0) throw new ArithmeticException("Módulo por cero");
return a % b;
}
static void sleep(double seconds) {
try { Thread.sleep((long) (seconds * 1000)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
}
}
El mismo algoritmo en Python
#!/usr/bin/env python3
"""Nivel 3 de Karel: explora el laberinto SIN conocer el mapa, usando DFS con memoria de posiciones visitadas (dos vectores paralelos) y una pila de direcciones para retroceder (backtracking) cuando se topa con un callejón sin salida. Si agota todo lo alcanzable sin encontrar la salida, concluye con certeza que es imposible — no es solo un heurístico que se rinde, es una exploración exhaustiva real."""
import math
import random
import sys
import time
import datetime
class DfdRuntimeError(Exception):
pass
def read_boolean():
t = input().strip().lower()
if t in ('verdadero', 'true', '1'):
return True
if t in ('falso', 'false', '0'):
return False
raise DfdRuntimeError('Se esperaba Verdadero o Falso, se recibio: ' + t)
def read_double():
return float(input().strip())
def read_vector():
return [float(t.strip()) for t in input().split(',') if t.strip() != '']
def fmt(d):
"""Formatea sin ".0" final cuando es entero — replica cómo freeDFDWeb (JS) imprime números."""
if math.isinf(d):
return 'Infinity' if d > 0 else '-Infinity'
if math.isnan(d):
return 'NaN'
if d == math.floor(d) and abs(d) < 1e15:
return str(int(d))
return str(d)
def bool_str(b):
"""`str(True)` da "True" (mayúscula) — el pseudolenguaje escribe e imprime "Verdadero"/"Falso"."""
return 'Verdadero' if b else 'Falso'
def sgn(x):
"""`Sgn(n)` de Flowgorithm. Devuelve x tal cual en el cero para conservar el signo del -0.0, igual que `Math.sign` de JS."""
if math.isnan(x):
return x
if x > 0:
return 1.0
if x < 0:
return -1.0
return x
def to_fixed(value, digits):
"""`ToFixed(r, i)` de Flowgorithm: TRUNCA a i decimales, no redondea."""
d = int(digits)
factor = 10 ** d
truncated = math.trunc(value * factor) / factor
return f'{truncated:.{d}f}'
def vec_get(v, index):
i = index - 1
if i < 0 or i >= len(v) or v[i] is None:
raise DfdRuntimeError(f'La posición {index} del vector no tiene un valor asignado todavía')
return v[i]
def vec_set(v, index, value):
i = index - 1
while len(v) <= i:
v.append(None)
v[i] = value
def div(a, b):
if b == 0:
raise DfdRuntimeError('División por cero')
return a / b
def mod(a, b):
if b == 0:
raise DfdRuntimeError('Módulo por cero')
# `math.fmod`, NUNCA el `%` nativo de Python — el signo del resultado de
# `%` en Python sigue al DIVISOR (-7 % 3 == 2), mientras que JS (y el
# intérprete real) usan resto con signo del DIVIDENDO (-7 % 3 == -1),
# que es exactamente lo que `math.fmod` reproduce.
return math.fmod(a, b)
def redon(x):
"""Redondeo tipo `Math.round` de JS (.5 siempre hacia +Infinity) — NUNCA `round()` nativo de Python (redondeo bancario: round(2.5) == 2)."""
return math.floor(x + 0.5)
def BuscarDireccion(filaAct, columnaAct, visFila, visColumna, n, direccionElegida, hayDireccion):
"""
Subprograma generado por freeDFDWeb, sin descripción en el diagrama original.
Args:
filaAct: float
columnaAct: float
visFila: float
visColumna: float
n: float
direccionElegida: float — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
hayDireccion: float — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
"""
orden = []
filaC = [0.0] # Lista de 1 elemento: simula paso por referencia (Python no permite mutar un float pasado a otra función) — se usa como "filaC[0]" en el resto de la función
columnaC = [0.0] # Lista de 1 elemento: simula paso por referencia (Python no permite mutar un float pasado a otra función) — se usa como "columnaC[0]" en el resto de la función
visitado = [0.0] # Lista de 1 elemento: simula paso por referencia (Python no permite mutar un float pasado a otra función) — se usa como "visitado[0]" en el resto de la función
vec_set(orden, int(1), "ARRIBA")
vec_set(orden, int(2), "DERECHA")
vec_set(orden, int(3), "ABAJO")
vec_set(orden, int(4), "IZQUIERDA")
hayDireccion[0] = 0
_for_start_1 = 1
_for_end_1 = 4
_for_step_1 = 1
if _for_step_1 == 0:
raise DfdRuntimeError('El incremento de "para i" no puede ser 0')
i = _for_start_1
while (i <= _for_end_1) if _for_step_1 > 0 else (i >= _for_end_1):
if (not (hayDireccion[0] != 0)):
d = vec_get(orden, int(i))
print("HayObstaculo(" + fmt(d) + ")") # Componente interactivo no disponible en el código exportado
if (not (response_call != 0)):
Delta(d, filaAct, columnaAct, filaC, columnaC)
EstaVisitado(filaC[0], columnaC[0], visFila, visColumna, n, visitado)
if (not (visitado[0] != 0)):
direccionElegida[0] = d
hayDireccion[0] = 1
i += _for_step_1
def Delta(direccion, filaAct, columnaAct, filaC, columnaC):
"""
Subprograma generado por freeDFDWeb, sin descripción en el diagrama original.
Args:
direccion: float
filaAct: float
columnaAct: float
filaC: float — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
columnaC: float — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
"""
_switch_2 = direccion
if _switch_2 == 0:
filaC[0] = (filaAct - 1)
columnaC[0] = columnaAct
elif _switch_2 == 0:
filaC[0] = (filaAct + 1)
columnaC[0] = columnaAct
elif _switch_2 == 0:
filaC[0] = filaAct
columnaC[0] = (columnaAct - 1)
elif _switch_2 == 0:
filaC[0] = filaAct
columnaC[0] = (columnaAct + 1)
else:
pass
def EstaVisitado(filaC, columnaC, visFila, visColumna, n, resultado):
"""
Subprograma generado por freeDFDWeb, sin descripción en el diagrama original.
Args:
filaC: float
columnaC: float
visFila: list[float]
visColumna: list[float]
n: float
resultado: float — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
"""
resultado[0] = 0
_for_start_3 = 1
_for_end_3 = n
_for_step_3 = 1
if _for_step_3 == 0:
raise DfdRuntimeError('El incremento de "para i" no puede ser 0')
i = _for_start_3
while (i <= _for_end_3) if _for_step_3 > 0 else (i >= _for_end_3):
if (not (resultado[0] != 0)):
if ((vec_get(visFila, int(i)) == filaC) and (vec_get(visColumna, int(i)) == columnaC)):
resultado[0] = 1
i += _for_step_3
def Opuesta(direccion, resultado):
"""
Subprograma generado por freeDFDWeb, sin descripción en el diagrama original.
Args:
direccion: float
resultado: str — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
"""
_switch_4 = direccion
if _switch_4 == 0:
resultado[0] = "ABAJO"
elif _switch_4 == 0:
resultado[0] = "ARRIBA"
elif _switch_4 == 0:
resultado[0] = "DERECHA"
elif _switch_4 == 0:
resultado[0] = "IZQUIERDA"
else:
pass
def main():
fila = [0.0] # Lista de 1 elemento: simula paso por referencia (Python no permite mutar un float pasado a otra función) — se usa como "fila[0]" en el resto de la función
columna = [0.0] # Lista de 1 elemento: simula paso por referencia (Python no permite mutar un float pasado a otra función) — se usa como "columna[0]" en el resto de la función
visFila = []
visColumna = []
direccionElegida = [0.0] # Lista de 1 elemento: simula paso por referencia (Python no permite mutar un float pasado a otra función) — se usa como "direccionElegida[0]" en el resto de la función
hayDireccion = [0.0] # Lista de 1 elemento: simula paso por referencia (Python no permite mutar un float pasado a otra función) — se usa como "hayDireccion[0]" en el resto de la función
pilaDir = []
direccionVolver = [0.0] # Lista de 1 elemento: simula paso por referencia (Python no permite mutar un float pasado a otra función) — se usa como "direccionVolver[0]" en el resto de la función
print("Activar(" + "Laberinto" + ")") # Componente interactivo no disponible en el código exportado
print("CargarEscenario(" + "default" + ")") # Componente interactivo no disponible en el código exportado
fila[0] = 1
columna[0] = 1
n = 1
vec_set(visFila, int(1), fila[0])
vec_set(visColumna, int(1), columna[0])
profundidad = 0
encontrado = 0
imposible = 0
while ((not (encontrado != 0)) and (not (imposible != 0))):
print("Llegue(" + "" + ")") # Componente interactivo no disponible en el código exportado
if (response_call != 0):
encontrado = 1
else:
BuscarDireccion(fila[0], columna[0], visFila, visColumna, n, direccionElegida, hayDireccion)
if (hayDireccion[0] != 0):
print("MOVER(" + fmt(direccionElegida[0]) + ", " + fmt(1) + ")") # Componente interactivo no disponible en el código exportado
Delta(direccionElegida[0], fila[0], columna[0], fila, columna)
n = (n + 1)
vec_set(visFila, int(n), fila[0])
vec_set(visColumna, int(n), columna[0])
profundidad = (profundidad + 1)
vec_set(pilaDir, int(profundidad), direccionElegida[0])
else:
if (profundidad == 0):
imposible = 1
else:
direccionTomada = vec_get(pilaDir, int(profundidad))
profundidad = (profundidad - 1)
Opuesta(direccionTomada, direccionVolver)
print("MOVER(" + fmt(direccionVolver[0]) + ", " + fmt(1) + ")") # Componente interactivo no disponible en el código exportado
Delta(direccionVolver[0], fila[0], columna[0], fila, columna)
if (encontrado != 0):
print("Encontré la salida — exploré " + fmt(n) + " posiciones.")
else:
print("Es IMPOSIBLE llegar a la salida — exploré todo lo alcanzable (" + fmt(n) + " posiciones) sin encontrarla.")
if __name__ == '__main__':
main()
El mismo algoritmo en C#
using System;
using System.Collections.Generic;
using System.Globalization;
/// <summary>Excepción propia para errores de ejecución del programa generado (división por cero, índice de vector sin asignar, etc.) — mismo criterio que el `RuntimeError` real de freeDFDWeb.</summary>
class DfdRuntimeException : Exception {
public DfdRuntimeException(string message) : base(message) { }
}
/// <summary>
/// Nivel 3 de Karel: explora el laberinto SIN conocer el mapa, usando DFS con memoria de posiciones visitadas (dos vectores paralelos) y una pila de direcciones para retroceder (backtracking) cuando se topa con un callejón sin salida. Si agota todo lo alcanzable sin encontrar la salida, concluye con certeza que es imposible — no es solo un heurístico que se rinde, es una exploración exhaustiva real.
/// </summary>
class Program {
/// <summary>
/// Punto de entrada del programa generado por freeDFDWeb.
/// </summary>
/// <param name="args">no utilizado — freeDFDWeb no pasa argumentos de línea de comandos.</param>
static void Main(string[] args) {
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
double fila = 0.0;
double columna = 0.0;
double n = 0.0;
List<double?> visFila = new List<double?>();
List<double?> visColumna = new List<double?>();
double profundidad = 0.0;
double encontrado = 0.0;
double imposible = 0.0;
double response_call = 0.0;
double direccionElegida = 0.0;
double hayDireccion = 0.0;
List<double?> pilaDir = new List<double?>();
double direccionTomada = 0.0;
double direccionVolver = 0.0;
Console.WriteLine("Activar(" + "Laberinto" + ")"); // Componente interactivo no disponible en el código exportado
Console.WriteLine("CargarEscenario(" + "default" + ")"); // Componente interactivo no disponible en el código exportado
fila = 1;
columna = 1;
n = 1;
VecSet(visFila, (int)(1), fila);
VecSet(visColumna, (int)(1), columna);
profundidad = 0;
encontrado = 0;
imposible = 0;
while ((!((encontrado) != 0) && !((imposible) != 0))) {
Console.WriteLine("Llegue(" + "" + ")"); // Componente interactivo no disponible en el código exportado
if (((response_call) != 0)) {
encontrado = 1;
} else {
BuscarDireccion(fila, columna, visFila, visColumna, n, ref direccionElegida, ref hayDireccion);
if (((hayDireccion) != 0)) {
Console.WriteLine("MOVER(" + Fmt(direccionElegida) + ", " + Fmt(1) + ")"); // Componente interactivo no disponible en el código exportado
Delta(direccionElegida, fila, columna, ref fila, ref columna);
n = (n + 1);
VecSet(visFila, (int)(n), fila);
VecSet(visColumna, (int)(n), columna);
profundidad = (profundidad + 1);
VecSet(pilaDir, (int)(profundidad), direccionElegida);
} else {
if ((profundidad == 0)) {
imposible = 1;
} else {
direccionTomada = VecGet(pilaDir, (int)(profundidad));
profundidad = (profundidad - 1);
Opuesta(direccionTomada, ref direccionVolver);
Console.WriteLine("MOVER(" + Fmt(direccionVolver) + ", " + Fmt(1) + ")"); // Componente interactivo no disponible en el código exportado
Delta(direccionVolver, fila, columna, ref fila, ref columna);
}
}
}
}
if (((encontrado) != 0)) {
Console.WriteLine("Encontré la salida — exploré " + Fmt(n) + " posiciones.");
} else {
Console.WriteLine("Es IMPOSIBLE llegar a la salida — exploré todo lo alcanzable (" + Fmt(n) + " posiciones) sin encontrarla.");
}
}
/// <summary>
/// Subprograma generado por freeDFDWeb, sin descripción en el diagrama original.
/// </summary>
/// <param name="filaAct">double</param>
/// <param name="columnaAct">double</param>
/// <param name="visFila">double</param>
/// <param name="visColumna">double</param>
/// <param name="n">double</param>
/// <param name="direccionElegida">double — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)</param>
/// <param name="hayDireccion">double — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)</param>
static void BuscarDireccion(double filaAct, double columnaAct, double visFila, double visColumna, double n, ref double direccionElegida, ref double hayDireccion) {
List<double?> orden = new List<double?>();
double i = 0.0;
double d = 0.0;
double response_call = 0.0;
double filaC = 0.0;
double columnaC = 0.0;
double visitado = 0.0;
VecSet(orden, (int)(1), "ARRIBA");
VecSet(orden, (int)(2), "DERECHA");
VecSet(orden, (int)(3), "ABAJO");
VecSet(orden, (int)(4), "IZQUIERDA");
hayDireccion = 0;
double _forStart1 = 1;
double _forEnd1 = 4;
double _forStep1 = 1;
if (_forStep1 == 0) { throw new DfdRuntimeException("El incremento de \"para i\" no puede ser 0"); }
for (i = _forStart1; _forStep1 > 0 ? i <= _forEnd1 : i >= _forEnd1; i += _forStep1) {
if (!((hayDireccion) != 0)) {
d = VecGet(orden, (int)(i));
Console.WriteLine("HayObstaculo(" + Fmt(d) + ")"); // Componente interactivo no disponible en el código exportado
if (!((response_call) != 0)) {
Delta(d, filaAct, columnaAct, ref filaC, ref columnaC);
EstaVisitado(filaC, columnaC, visFila, visColumna, n, ref visitado);
if (!((visitado) != 0)) {
direccionElegida = d;
hayDireccion = 1;
}
}
}
}
}
/// <summary>
/// Subprograma generado por freeDFDWeb, sin descripción en el diagrama original.
/// </summary>
/// <param name="direccion">double</param>
/// <param name="filaAct">double</param>
/// <param name="columnaAct">double</param>
/// <param name="filaC">double — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)</param>
/// <param name="columnaC">double — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)</param>
static void Delta(double direccion, double filaAct, double columnaAct, ref double filaC, ref double columnaC) {
double _switch2 = direccion;
if (_switch2 == 0.0) {
filaC = (filaAct - 1);
columnaC = columnaAct;
} else if (_switch2 == 0.0) {
filaC = (filaAct + 1);
columnaC = columnaAct;
} else if (_switch2 == 0.0) {
filaC = filaAct;
columnaC = (columnaAct - 1);
} else if (_switch2 == 0.0) {
filaC = filaAct;
columnaC = (columnaAct + 1);
} else {
}
}
/// <summary>
/// Subprograma generado por freeDFDWeb, sin descripción en el diagrama original.
/// </summary>
/// <param name="filaC">double</param>
/// <param name="columnaC">double</param>
/// <param name="visFila">List<double?></param>
/// <param name="visColumna">List<double?></param>
/// <param name="n">double</param>
/// <param name="resultado">double — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)</param>
static void EstaVisitado(double filaC, double columnaC, List<double?> visFila, List<double?> visColumna, double n, ref double resultado) {
double i = 0.0;
resultado = 0;
double _forStart3 = 1;
double _forEnd3 = n;
double _forStep3 = 1;
if (_forStep3 == 0) { throw new DfdRuntimeException("El incremento de \"para i\" no puede ser 0"); }
for (i = _forStart3; _forStep3 > 0 ? i <= _forEnd3 : i >= _forEnd3; i += _forStep3) {
if (!((resultado) != 0)) {
if (((VecGet(visFila, (int)(i)) == filaC) && (VecGet(visColumna, (int)(i)) == columnaC))) {
resultado = 1;
}
}
}
}
/// <summary>
/// Subprograma generado por freeDFDWeb, sin descripción en el diagrama original.
/// </summary>
/// <param name="direccion">double</param>
/// <param name="resultado">string — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)</param>
static void Opuesta(double direccion, ref string resultado) {
double _switch4 = direccion;
if (_switch4 == 0.0) {
resultado = "ABAJO";
} else if (_switch4 == 0.0) {
resultado = "ARRIBA";
} else if (_switch4 == 0.0) {
resultado = "DERECHA";
} else if (_switch4 == 0.0) {
resultado = "IZQUIERDA";
} else {
}
}
static readonly Random Rnd = new Random();
static bool ReadBoolean() {
string t = (Console.ReadLine() ?? "").Trim().ToLowerInvariant();
if (t == "verdadero" || t == "true" || t == "1") return true;
if (t == "falso" || t == "false" || t == "0") return false;
throw new Exception("Se esperaba Verdadero o Falso, se recibio: " + t);
}
static double ReadDouble() {
return double.Parse(Console.ReadLine().Trim(), CultureInfo.InvariantCulture);
}
static List<double?> ReadVector() {
List<double?> result = new List<double?>();
foreach (string token in Console.ReadLine().Split(',')) {
string t = token.Trim();
if (t.Length > 0) result.Add(double.Parse(t, CultureInfo.InvariantCulture));
}
return result;
}
/// <summary>Formatea un double sin ".0" final cuando es entero — replica cómo freeDFDWeb (JS) imprime números.</summary>
static string Fmt(double d) {
if (double.IsPositiveInfinity(d)) return "Infinity";
if (double.IsNegativeInfinity(d)) return "-Infinity";
if (double.IsNaN(d)) return "NaN";
if (d == Math.Floor(d) && Math.Abs(d) < 1e15) {
return ((long)d).ToString(CultureInfo.InvariantCulture);
}
return d.ToString(CultureInfo.InvariantCulture);
}
/// <summary>`true.ToString()` da "True" (mayúscula) — el pseudolenguaje escribe e imprime "Verdadero"/"Falso".</summary>
static string BoolStr(bool b) {
return b ? "Verdadero" : "Falso";
}
/// <summary>`Sgn(n)` de Flowgorithm. Helper y no `Math.Sign`: ese LANZA sobre NaN, mientras el intérprete devuelve NaN. Devuelve x en el cero para conservar el -0.0.</summary>
static double Sgn(double x) {
if (double.IsNaN(x)) return x;
if (x > 0) return 1.0;
if (x < 0) return -1.0;
return x;
}
/// <summary>`ToFixed(r, i)` de Flowgorithm: TRUNCA a i decimales, no redondea. `InvariantCulture` para que el separador sea el punto, igual que `Fmt`.</summary>
static string ToFixed(double value, double digits) {
int d = (int) digits;
double factor = Math.Pow(10, d);
double truncated = Math.Truncate(value * factor) / factor;
return truncated.ToString("F" + d, CultureInfo.InvariantCulture);
}
static double VecGet(List<double?> v, int index) {
int i = index - 1;
if (i < 0 || i >= v.Count || !v[i].HasValue) {
throw new DfdRuntimeException("La posición " + index + " del vector no tiene un valor asignado todavía");
}
return v[i].Value;
}
static void VecSet(List<double?> v, int index, double value) {
int i = index - 1;
while (v.Count <= i) v.Add(null);
v[i] = value;
}
static double Div(double a, double b) {
if (b == 0) throw new DfdRuntimeException("División por cero");
return a / b;
}
static double Mod(double a, double b) {
if (b == 0) throw new DfdRuntimeException("Módulo por cero");
// El operador `%` nativo de C# para `double` SÍ coincide con el
// signo de JS (confirmado real: -7 % 3 == -1 en ambos) — a
// diferencia de Python, no hace falta ningún wrapper de signo.
return a % b;
}
/// <summary>Redondeo tipo `Math.round` de JS (.5 siempre hacia +Infinity) — NUNCA `Math.Round()` directo (bancario en .NET: Math.Round(2.5) == 2).</summary>
static double Redon(double x) {
return Math.Floor(x + 0.5);
}
}
El mismo algoritmo en C++
#include <iostream>
#include <string>
#include <vector>
#include <optional>
#include <cmath>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <chrono>
#include <thread>
#include <charconv>
#include <stdexcept>
/**
* Nivel 3 de Karel: explora el laberinto SIN conocer el mapa, usando DFS con memoria de posiciones visitadas (dos vectores paralelos) y una pila de direcciones para retroceder (backtracking) cuando se topa con un callejón sin salida. Si agota todo lo alcanzable sin encontrar la salida, concluye con certeza que es imposible — no es solo un heurístico que se rinde, es una exploración exhaustiva real.
*/
const double PI = 3.14159265358979323846;
class DfdRuntimeError : public std::runtime_error {
public:
explicit DfdRuntimeError(const std::string& message) : std::runtime_error(message) {}
};
std::string readLine() {
std::string line;
if (!std::getline(std::cin, line)) return "";
if (!line.empty() && line.back() == '\r') line.pop_back();
return line;
}
/** `std::stod` (usado por `toNumberOrThrow`) NO tira con texto no numérico de forma consistente en todos los casos (ignora sufijos raros) pero SÍ tira `std::invalid_argument` con texto totalmente no numérico — verificado real. Envuelto para relanzar como `DfdRuntimeError`, mismo tipo de excepción que el resto del programa generado. */
double toNumberOrThrow(const std::string& value) {
try {
return std::stod(value);
} catch (const std::exception&) {
throw DfdRuntimeError("Se esperaba un número, se encontró \"" + value + "\"");
}
}
bool readBoolean() {
std::string t = readLine();
for (auto& c : t) c = (char)std::tolower((unsigned char)c);
if (t == "verdadero" || t == "true" || t == "1") return true;
if (t == "falso" || t == "false" || t == "0") return false;
throw std::runtime_error("Se esperaba Verdadero o Falso, se recibio: " + t);
}
double readDouble() {
return toNumberOrThrow(readLine());
}
std::vector<std::optional<double>> readVector() {
std::vector<std::optional<double>> result;
std::stringstream ss(readLine());
std::string token;
while (std::getline(ss, token, ',')) {
size_t start = token.find_first_not_of(" \t");
size_t end = token.find_last_not_of(" \t");
if (start == std::string::npos) continue;
result.push_back(toNumberOrThrow(token.substr(start, end - start + 1)));
}
return result;
}
/** Formatea sin ".0" final cuando es entero, con representación de ida y vuelta exacta para decimales (`std::to_chars`, C++17) — replica cómo freeDFDWeb (JS) imprime números. Diverge de JS solo en notación científica de magnitudes extremas (ver comentario de cabecera del archivo), gap aceptado. */
std::string fmt(double d) {
if (std::isinf(d)) return d > 0 ? "Infinity" : "-Infinity";
if (std::isnan(d)) return "NaN";
char buf[64];
if (d == std::floor(d) && std::abs(d) < 1e15) {
auto res = std::to_chars(buf, buf + sizeof(buf), (long long)d);
return std::string(buf, res.ptr);
}
auto res = std::to_chars(buf, buf + sizeof(buf), d);
return std::string(buf, res.ptr);
}
/** `std::cout << bool` imprime "1"/"0" — el pseudolenguaje escribe e imprime "Verdadero"/"Falso". */
std::string boolStr(bool b) {
return b ? "Verdadero" : "Falso";
}
/** `Sgn(n)` de Flowgorithm. Devuelve x en el cero para conservar el signo del -0.0, igual que `Math.sign` de JS. */
double sgn(double x) {
if (std::isnan(x)) return x;
if (x > 0) return 1.0;
if (x < 0) return -1.0;
return x;
}
/** `ToChar(n)`: punto de código → carácter. C++ es el único destino sin conversión nativa, así que codifica UTF-8 a mano — `std::string` acá son bytes, no puntos de código. */
std::string toCharStr(double code) {
long long c = (long long) code;
if (c < 0 || c > 0x10FFFF) {
throw DfdRuntimeError("ToChar(" + std::to_string(c) + "): codigo de caracter fuera de rango");
}
std::string out;
if (c < 0x80) {
out += (char)c;
} else if (c < 0x800) {
out += (char)(0xC0 | (c >> 6));
out += (char)(0x80 | (c & 0x3F));
} else if (c < 0x10000) {
out += (char)(0xE0 | (c >> 12));
out += (char)(0x80 | ((c >> 6) & 0x3F));
out += (char)(0x80 | (c & 0x3F));
} else {
out += (char)(0xF0 | (c >> 18));
out += (char)(0x80 | ((c >> 12) & 0x3F));
out += (char)(0x80 | ((c >> 6) & 0x3F));
out += (char)(0x80 | (c & 0x3F));
}
return out;
}
/** `ToCode(c)`: inversa de `toCharStr` — decodifica el PRIMER punto de código UTF-8, no el primer byte (si no, una "ñ" daría 195). */
double toCode(const std::string& s) {
if (s.empty()) {
throw DfdRuntimeError("ToCode espera un texto de un caracter");
}
unsigned char b0 = (unsigned char)s[0];
if (b0 < 0x80) return (double)b0;
int extra = (b0 >= 0xF0) ? 3 : (b0 >= 0xE0) ? 2 : 1;
long long cp = b0 & (0x3F >> extra);
for (int i = 1; i <= extra; i++) {
if ((size_t)i >= s.size()) {
throw DfdRuntimeError("ToCode: texto UTF-8 mal formado");
}
cp = (cp << 6) | ((unsigned char)s[i] & 0x3F);
}
return (double)cp;
}
/** `ToFixed(r, i)` de Flowgorithm: TRUNCA a i decimales, no redondea. */
std::string toFixed(double value, double digits) {
int d = (int) digits;
double factor = std::pow(10.0, d);
double truncated = std::trunc(value * factor) / factor;
std::ostringstream out;
out << std::fixed << std::setprecision(d) << truncated;
return out.str();
}
std::string toUpperStr(const std::string& s) {
std::string result = s;
std::transform(result.begin(), result.end(), result.begin(), [](unsigned char c) { return std::toupper(c); });
return result;
}
std::string toLowerStr(const std::string& s) {
std::string result = s;
std::transform(result.begin(), result.end(), result.begin(), [](unsigned char c) { return std::tolower(c); });
return result;
}
double vecGet(std::vector<std::optional<double>>& v, int index) {
int i = index - 1;
if (i < 0 || (size_t)i >= v.size() || !v[i].has_value()) {
throw DfdRuntimeError("La posición " + std::to_string(index) + " del vector no tiene un valor asignado todavía");
}
return *v[i];
}
void vecSet(std::vector<std::optional<double>>& v, int index, double value) {
int i = index - 1;
if ((size_t)(i + 1) > v.size()) v.resize(i + 1);
v[i] = value;
}
/**
* `dividir` y no `div`, que sería el nombre obvio: `div` YA EXISTE en la
* biblioteca estándar de C (`<cstdlib>`, devuelve un `div_t`) y está
* declarada aunque no se incluya ese encabezado. Con dos literales
* enteros, `div(7, 2)` resuelve a la versión de C —coincidencia exacta—
* y el programa generado NO COMPILA:
*
* error: cannot convert 'div_t' to 'double'
*
* O sea: CUALQUIER programa con una división fallaba al exportar a C++.
* Encontrado el 30-oct-2026 compilando de verdad lo exportado
* (`scripts/equivalencia/comparar.mjs`); ninguna prueba lo veía porque
* ninguna compilaba C++.
*/
double dividir(double a, double b) {
if (b == 0) throw DfdRuntimeError("División por cero");
return a / b;
}
double mod(double a, double b) {
if (b == 0) throw DfdRuntimeError("Módulo por cero");
// `%` NO COMPILA sobre `double` en C++ — `std::fmod` es la única forma.
// Su signo SÍ coincide con JS (confirmado real), a diferencia de
// Python, que necesitaba un wrapper de signo además del de cero.
return std::fmod(a, b);
}
/** Redondeo tipo `Math.round` de JS (.5 siempre hacia +Infinity) — NUNCA `std::round()` directo (redondea siempre alejándose de cero: round(-2.5) == -3, distinto de Math.round(-2.5) == -2). */
double redon(double x) {
return std::floor(x + 0.5);
}
// ---------------------------------------------------------------------------
// Programa
// ---------------------------------------------------------------------------
/**
* Subprograma generado por freeDFDWeb, sin descripción en el diagrama original.
* @param filaAct double
* @param columnaAct double
* @param visFila double
* @param visColumna double
* @param n double
* @param direccionElegida double — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
* @param hayDireccion double — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
*/
void BuscarDireccion(double filaAct, double columnaAct, double visFila, double visColumna, double n, double& direccionElegida, double& hayDireccion) {
std::vector<std::optional<double>> orden;
double i = 0.0;
double d = 0.0;
double response_call = 0.0;
double filaC = 0.0;
double columnaC = 0.0;
double visitado = 0.0;
vecSet(orden, (int)(1), std::string("ARRIBA"));
vecSet(orden, (int)(2), std::string("DERECHA"));
vecSet(orden, (int)(3), std::string("ABAJO"));
vecSet(orden, (int)(4), std::string("IZQUIERDA"));
hayDireccion = 0;
double _forStart1 = 1;
double _forEnd1 = 4;
double _forStep1 = 1;
if (_forStep1 == 0) { throw DfdRuntimeError("El incremento de \"para i\" no puede ser 0"); }
for (i = _forStart1; _forStep1 > 0 ? i <= _forEnd1 : i >= _forEnd1; i += _forStep1) {
if (!((hayDireccion) != 0)) {
d = vecGet(orden, (int)(i));
std::cout << (std::string("HayObstaculo(") + fmt(d) + ")") << "\n"; // Componente interactivo no disponible en el código exportado
if (!((response_call) != 0)) {
Delta(d, filaAct, columnaAct, filaC, columnaC);
EstaVisitado(filaC, columnaC, visFila, visColumna, n, visitado);
if (!((visitado) != 0)) {
direccionElegida = d;
hayDireccion = 1;
}
}
}
}
}
/**
* Subprograma generado por freeDFDWeb, sin descripción en el diagrama original.
* @param direccion double
* @param filaAct double
* @param columnaAct double
* @param filaC double — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
* @param columnaC double — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
*/
void Delta(double direccion, double filaAct, double columnaAct, double& filaC, double& columnaC) {
double _switch2 = direccion;
if (_switch2 == 0.0) {
filaC = (filaAct - 1);
columnaC = columnaAct;
} else if (_switch2 == 0.0) {
filaC = (filaAct + 1);
columnaC = columnaAct;
} else if (_switch2 == 0.0) {
filaC = filaAct;
columnaC = (columnaAct - 1);
} else if (_switch2 == 0.0) {
filaC = filaAct;
columnaC = (columnaAct + 1);
} else {
}
}
/**
* Subprograma generado por freeDFDWeb, sin descripción en el diagrama original.
* @param filaC double
* @param columnaC double
* @param visFila std::vector<std::optional<double>>
* @param visColumna std::vector<std::optional<double>>
* @param n double
* @param resultado double — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
*/
void EstaVisitado(double filaC, double columnaC, std::vector<std::optional<double>> visFila, std::vector<std::optional<double>> visColumna, double n, double& resultado) {
double i = 0.0;
resultado = 0;
double _forStart3 = 1;
double _forEnd3 = n;
double _forStep3 = 1;
if (_forStep3 == 0) { throw DfdRuntimeError("El incremento de \"para i\" no puede ser 0"); }
for (i = _forStart3; _forStep3 > 0 ? i <= _forEnd3 : i >= _forEnd3; i += _forStep3) {
if (!((resultado) != 0)) {
if (((vecGet(visFila, (int)(i)) == filaC) && (vecGet(visColumna, (int)(i)) == columnaC))) {
resultado = 1;
}
}
}
}
/**
* Subprograma generado por freeDFDWeb, sin descripción en el diagrama original.
* @param direccion double
* @param resultado std::string — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
*/
void Opuesta(double direccion, std::string& resultado) {
double _switch4 = direccion;
if (_switch4 == 0.0) {
resultado = std::string("ABAJO");
} else if (_switch4 == 0.0) {
resultado = std::string("ARRIBA");
} else if (_switch4 == 0.0) {
resultado = std::string("DERECHA");
} else if (_switch4 == 0.0) {
resultado = std::string("IZQUIERDA");
} else {
}
}
/**
* Punto de entrada del programa generado por freeDFDWeb.
*/
int main() {
double fila = 0.0;
double columna = 0.0;
double n = 0.0;
std::vector<std::optional<double>> visFila;
std::vector<std::optional<double>> visColumna;
double profundidad = 0.0;
double encontrado = 0.0;
double imposible = 0.0;
double response_call = 0.0;
double direccionElegida = 0.0;
double hayDireccion = 0.0;
std::vector<std::optional<double>> pilaDir;
double direccionTomada = 0.0;
double direccionVolver = 0.0;
std::cout << (std::string("Activar(") + std::string("Laberinto") + ")") << "\n"; // Componente interactivo no disponible en el código exportado
std::cout << (std::string("CargarEscenario(") + std::string("default") + ")") << "\n"; // Componente interactivo no disponible en el código exportado
fila = 1;
columna = 1;
n = 1;
vecSet(visFila, (int)(1), fila);
vecSet(visColumna, (int)(1), columna);
profundidad = 0;
encontrado = 0;
imposible = 0;
while ((!((encontrado) != 0) && !((imposible) != 0))) {
std::cout << (std::string("Llegue(") + std::string("") + ")") << "\n"; // Componente interactivo no disponible en el código exportado
if (((response_call) != 0)) {
encontrado = 1;
} else {
BuscarDireccion(fila, columna, visFila, visColumna, n, direccionElegida, hayDireccion);
if (((hayDireccion) != 0)) {
std::cout << (std::string("MOVER(") + fmt(direccionElegida) + ", " + fmt(1) + ")") << "\n"; // Componente interactivo no disponible en el código exportado
Delta(direccionElegida, fila, columna, fila, columna);
n = (n + 1);
vecSet(visFila, (int)(n), fila);
vecSet(visColumna, (int)(n), columna);
profundidad = (profundidad + 1);
vecSet(pilaDir, (int)(profundidad), direccionElegida);
} else {
if ((profundidad == 0)) {
imposible = 1;
} else {
direccionTomada = vecGet(pilaDir, (int)(profundidad));
profundidad = (profundidad - 1);
Opuesta(direccionTomada, direccionVolver);
std::cout << (std::string("MOVER(") + fmt(direccionVolver) + ", " + fmt(1) + ")") << "\n"; // Componente interactivo no disponible en el código exportado
Delta(direccionVolver, fila, columna, fila, columna);
}
}
}
}
if (((encontrado) != 0)) {
std::cout << std::string("Encontré la salida — exploré ") + fmt(n) + std::string(" posiciones.") << "\n";
} else {
std::cout << std::string("Es IMPOSIBLE llegar a la salida — exploré todo lo alcanzable (") + fmt(n) + std::string(" posiciones) sin encontrarla.") << "\n";
}
return 0;
}
El mismo algoritmo en TypeScript
import * as fs from 'fs';
/**
* Nivel 3 de Karel: explora el laberinto SIN conocer el mapa, usando DFS con memoria de posiciones visitadas (dos vectores paralelos) y una pila de direcciones para retroceder (backtracking) cuando se topa con un callejón sin salida. Si agota todo lo alcanzable sin encontrar la salida, concluye con certeza que es imposible — no es solo un heurístico que se rinde, es una exploración exhaustiva real.
*/
class DfdRuntimeError extends Error {}
const inputLines: string[] = fs.readFileSync(0, 'utf-8').split('\n');
let inputIndex = 0;
/** Toda la entrada se lee de una sola vez al arrancar (ver cabecera del archivo) — cada llamada consume la siguiente línea ya bufferizada. */
function readLine(): string {
return (inputLines[inputIndex++] ?? '').replace(/\r$/, '');
}
function readBoolean(): boolean {
const t = readLine().trim().toLowerCase();
if (t === 'verdadero' || t === 'true' || t === '1') return true;
if (t === 'falso' || t === 'false' || t === '0') return false;
throw new Error('Se esperaba Verdadero o Falso, se recibio: ' + t);
}
function readDouble(): number {
return toNumberOrThrow(readLine().trim());
}
function readVector(): (number | undefined)[] {
return readLine()
.split(',')
.map((t) => t.trim())
.filter((t) => t.length > 0)
.map((t) => toNumberOrThrow(t));
}
/** `Number(x)` nativo no tira error con texto no numérico (da `NaN`) — a diferencia del intérprete real (`toNumber`), que sí tira. Único punto donde este generador necesita reproducir ese comportamiento a mano. */
function toNumberOrThrow(value: string | number): number {
const n = Number(value);
if (Number.isNaN(n)) {
throw new DfdRuntimeError(`Se esperaba un número, se encontró "${value}"`);
}
return n;
}
/** `String(true)` da "true" — el pseudolenguaje escribe e imprime "Verdadero"/"Falso". */
function boolStr(b: boolean): string {
return b ? 'Verdadero' : 'Falso';
}
/** `ToFixed(r, i)` de Flowgorithm: TRUNCA a i decimales, no redondea — por eso no basta el `.toFixed()` nativo, que redondea. */
function toFixed(value: number, digits: number): string {
const d = Math.trunc(digits);
const factor = Math.pow(10, d);
return (Math.trunc(value * factor) / factor).toFixed(d);
}
function vecGet(v: (number | undefined)[], index: number): number {
const i = Math.trunc(index) - 1;
const value = v[i];
if (i < 0 || value === undefined) {
throw new DfdRuntimeError(`La posición ${index} del vector no tiene un valor asignado todavía`);
}
return value;
}
function vecSet(v: (number | undefined)[], index: number, value: number): void {
v[Math.trunc(index) - 1] = value;
}
function div(a: number, b: number): number {
if (b === 0) throw new DfdRuntimeError('División por cero');
return a / b;
}
function mod(a: number, b: number): number {
if (b === 0) throw new DfdRuntimeError('Módulo por cero');
// El `%` nativo de JS/TS coincide exacto con el del intérprete real
// (mismo motor) — a diferencia de Python, no hace falta ningún wrapper
// de signo, solo el chequeo de división por cero.
return a % b;
}
/**
* Generado por freeDFDWeb, sin descripción en el diagrama original.
* @param filaAct number
* @param columnaAct number
* @param visFila number
* @param visColumna number
* @param n number
* @param direccionElegida number[] — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
* @param hayDireccion number[] — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
*/
function BuscarDireccion(filaAct: number, columnaAct: number, visFila: number, visColumna: number, n: number, direccionElegida: number[], hayDireccion: number[]): void {
let orden: (number | undefined)[] = [];
let i: number = 0;
let d: number = 0;
let response_call: number = 0;
let filaC: number[] = [0]; // Arreglo de 1 elemento: simula paso por referencia (JS/TS no tiene parámetros por referencia para primitivos) — se usa como "filaC[0]" en el resto de la función
let columnaC: number[] = [0]; // Arreglo de 1 elemento: simula paso por referencia (JS/TS no tiene parámetros por referencia para primitivos) — se usa como "columnaC[0]" en el resto de la función
let visitado: number[] = [0]; // Arreglo de 1 elemento: simula paso por referencia (JS/TS no tiene parámetros por referencia para primitivos) — se usa como "visitado[0]" en el resto de la función
vecSet(orden, 1, "ARRIBA");
vecSet(orden, 2, "DERECHA");
vecSet(orden, 3, "ABAJO");
vecSet(orden, 4, "IZQUIERDA");
hayDireccion[0] = 0;
const _forStart1 = 1;
const _forEnd1 = 4;
const _forStep1 = 1;
if (_forStep1 === 0) { throw new DfdRuntimeError("El incremento de \"para i\" no puede ser 0"); }
for (i = _forStart1; _forStep1 > 0 ? i <= _forEnd1 : i >= _forEnd1; i += _forStep1) {
if (!((hayDireccion[0]) !== 0)) {
d = vecGet(orden, i);
console.log("HayObstaculo(" + String(d) + ")"); // Componente interactivo no disponible en el código exportado
if (!((response_call) !== 0)) {
Delta(d, filaAct, columnaAct, filaC, columnaC);
EstaVisitado(filaC[0], columnaC[0], visFila, visColumna, n, visitado);
if (!((visitado[0]) !== 0)) {
direccionElegida[0] = d;
hayDireccion[0] = 1;
}
}
}
}
}
/**
* Generado por freeDFDWeb, sin descripción en el diagrama original.
* @param direccion number
* @param filaAct number
* @param columnaAct number
* @param filaC number[] — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
* @param columnaC number[] — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
*/
function Delta(direccion: number, filaAct: number, columnaAct: number, filaC: number[], columnaC: number[]): void {
const _switch2: number = direccion;
if (_switch2 === 0) {
filaC[0] = (filaAct - 1);
columnaC[0] = columnaAct;
} else if (_switch2 === 0) {
filaC[0] = (filaAct + 1);
columnaC[0] = columnaAct;
} else if (_switch2 === 0) {
filaC[0] = filaAct;
columnaC[0] = (columnaAct - 1);
} else if (_switch2 === 0) {
filaC[0] = filaAct;
columnaC[0] = (columnaAct + 1);
} else {
}
}
/**
* Generado por freeDFDWeb, sin descripción en el diagrama original.
* @param filaC number
* @param columnaC number
* @param visFila (number | undefined)[]
* @param visColumna (number | undefined)[]
* @param n number
* @param resultado number[] — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
*/
function EstaVisitado(filaC: number, columnaC: number, visFila: (number | undefined)[], visColumna: (number | undefined)[], n: number, resultado: number[]): void {
let i: number = 0;
resultado[0] = 0;
const _forStart3 = 1;
const _forEnd3 = n;
const _forStep3 = 1;
if (_forStep3 === 0) { throw new DfdRuntimeError("El incremento de \"para i\" no puede ser 0"); }
for (i = _forStart3; _forStep3 > 0 ? i <= _forEnd3 : i >= _forEnd3; i += _forStep3) {
if (!((resultado[0]) !== 0)) {
if (((vecGet(visFila, i) === filaC) && (vecGet(visColumna, i) === columnaC))) {
resultado[0] = 1;
}
}
}
}
/**
* Generado por freeDFDWeb, sin descripción en el diagrama original.
* @param direccion number
* @param resultado string[] — por referencia (el subprograma lo reasigna; el valor final es visible para quien llama)
*/
function Opuesta(direccion: number, resultado: string[]): void {
const _switch4: number = direccion;
if (_switch4 === 0) {
resultado[0] = "ABAJO";
} else if (_switch4 === 0) {
resultado[0] = "ARRIBA";
} else if (_switch4 === 0) {
resultado[0] = "DERECHA";
} else if (_switch4 === 0) {
resultado[0] = "IZQUIERDA";
} else {
}
}
/**
* Punto de entrada del programa generado por freeDFDWeb.
*/
function main(): void {
let fila: number[] = [0]; // Arreglo de 1 elemento: simula paso por referencia (JS/TS no tiene parámetros por referencia para primitivos) — se usa como "fila[0]" en el resto de la función
let columna: number[] = [0]; // Arreglo de 1 elemento: simula paso por referencia (JS/TS no tiene parámetros por referencia para primitivos) — se usa como "columna[0]" en el resto de la función
let n: number = 0;
let visFila: (number | undefined)[] = [];
let visColumna: (number | undefined)[] = [];
let profundidad: number = 0;
let encontrado: number = 0;
let imposible: number = 0;
let response_call: number = 0;
let direccionElegida: number[] = [0]; // Arreglo de 1 elemento: simula paso por referencia (JS/TS no tiene parámetros por referencia para primitivos) — se usa como "direccionElegida[0]" en el resto de la función
let hayDireccion: number[] = [0]; // Arreglo de 1 elemento: simula paso por referencia (JS/TS no tiene parámetros por referencia para primitivos) — se usa como "hayDireccion[0]" en el resto de la función
let pilaDir: (number | undefined)[] = [];
let direccionTomada: number = 0;
let direccionVolver: number[] = [0]; // Arreglo de 1 elemento: simula paso por referencia (JS/TS no tiene parámetros por referencia para primitivos) — se usa como "direccionVolver[0]" en el resto de la función
console.log("Activar(" + "Laberinto" + ")"); // Componente interactivo no disponible en el código exportado
console.log("CargarEscenario(" + "default" + ")"); // Componente interactivo no disponible en el código exportado
fila[0] = 1;
columna[0] = 1;
n = 1;
vecSet(visFila, 1, fila[0]);
vecSet(visColumna, 1, columna[0]);
profundidad = 0;
encontrado = 0;
imposible = 0;
while ((!((encontrado) !== 0) && !((imposible) !== 0))) {
console.log("Llegue(" + "" + ")"); // Componente interactivo no disponible en el código exportado
if (((response_call) !== 0)) {
encontrado = 1;
} else {
BuscarDireccion(fila[0], columna[0], visFila, visColumna, n, direccionElegida, hayDireccion);
if (((hayDireccion[0]) !== 0)) {
console.log("MOVER(" + String(direccionElegida[0]) + ", " + String(1) + ")"); // Componente interactivo no disponible en el código exportado
Delta(direccionElegida[0], fila[0], columna[0], fila, columna);
n = (n + 1);
vecSet(visFila, n, fila[0]);
vecSet(visColumna, n, columna[0]);
profundidad = (profundidad + 1);
vecSet(pilaDir, profundidad, direccionElegida[0]);
} else {
if ((profundidad === 0)) {
imposible = 1;
} else {
direccionTomada = vecGet(pilaDir, profundidad);
profundidad = (profundidad - 1);
Opuesta(direccionTomada, direccionVolver);
console.log("MOVER(" + String(direccionVolver[0]) + ", " + String(1) + ")"); // Componente interactivo no disponible en el código exportado
Delta(direccionVolver[0], fila[0], columna[0], fila, columna);
}
}
}
}
if (((encontrado) !== 0)) {
console.log("Encontré la salida — exploré " + String(n) + " posiciones.");
} else {
console.log("Es IMPOSIBLE llegar a la salida — exploré todo lo alcanzable (" + String(n) + " posiciones) sin encontrarla.");
}
}
main();
Generado por freeDFDWeb desde el diagrama de arriba. Es el mismo archivo que descarga el botón Exportar del editor.
Abre y guarda en los formatos que ya usas
Importa y exporta PSeInt (.psc) y Flowgorithm (.fprg), y abre archivos .dfd del DFD clásico. Todo en el navegador, sin instalar nada: sirve en Windows, Mac, Linux, Chromebook y celular.