El generador de Autómatas Finitos Determinísticos (AFD) es una herramienta diseñada para crear visualmente y representar autómatas finitos determinísticos. Este código HTML con JavaScript permite a los usuarios ingresar los estados del autómata y las transiciones entre estos, generando una representación gráfica interactiva.
HTML: El código HTML proporciona la estructura básica de la página web. Define los elementos de entrada para los estados y las transiciones, así como un espacio para visualizar el autómata generado.
CSS: Contiene estilos para dar formato y presentación a los elementos HTML, adaptándolos para dispositivos móviles y de escritorio. Define la apariencia de los estados, las transiciones y la disposición general de la interfaz.
JavaScript: El código JavaScript maneja la lógica detrás de la generación y representación del autómata. Gestiona la creación de estados, transiciones, líneas de conexión y etiquetas asociadas a las transiciones.
Ingreso de Estados: Permite a los usuarios ingresar una lista de estados separados por coma. El primer estado ingresado se considera el estado inicial y el último, el estado final.
Ingreso de Transiciones: Los usuarios pueden ingresar las transiciones en el formato "inicio:valor:fin", representando la transición desde un estado de inicio a un estado final con un valor específico.
Representación Gráfica: Genera visualmente los estados como círculos en un espacio gráfico, con la capacidad de definir estados iniciales y finales. Crea líneas de conexión entre los estados según las transiciones ingresadas.
Interacción Interactiva: Los estados y las transiciones son elementos interactivos, permitiendo arrastrar estados para reorganizar su posición y visualizar las transiciones asociadas.
generarEstados(): Maneja la lógica para generar y representar visualmente los estados ingresados por el usuario.
startTransition(), stopTransition(), createTransition(): Funciones que gestionan la creación de transiciones entre estados y su representación gráfica.
Interfaz de Usuario: Podría mejorarse con una interfaz más intuitiva para la manipulación de estados y transiciones.
Validación de Entrada: Actualmente, no hay validación exhaustiva de la entrada de estados y transiciones.
Optimización de Código: Se pueden realizar mejoras para optimizar el rendimiento y la legibilidad del código JavaScript.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Generador de AFD</title>
<style>
/* Estilos CSS para dispositivos móviles y escritorio */
body {
font-family: 'Roboto Mono', monospace;
color: #0041a5;
margin: 20px;
position: relative;
}
h1 {
text-align: center;
}
p {
font-weight: bold;
}
label, input[type="text"], button {
display: block;
margin: 10px 0;
width: 100%;
box-sizing: border-box;
}
input[type="text"] {
padding: 5px;
}
button {
padding: 8px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #0056b3;
}
#afdGraph {
width: 100%;
max-width: 800px;
height: 400px;
border: 1px solid #ccc;
margin: 20px auto;
position: relative;
background-image: linear-gradient(#ccc 1px, transparent 1px), linear-gradient(90deg, #ccc 1px, transparent 1px);
background-size: 40px 40px;
}
.estado {
position: absolute;
width: 40px;
height: 40px;
border: 2px solid #000;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background-color: #ccc;
cursor: pointer;
}
.estado.inicial {
border-color: green;
}
.estado.final {
border-color: red;
}
.transition-line {
position: absolute;
border-bottom: 2px solid black;
transform-origin: left top;
}
.transition-label {
display: none;
}
.transition-arrow {
position: absolute;
width: 0;
height: 0;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
border-right: 12px solid black;
transform-origin: 100% 50%;
transition: transform 0.3s ease;
}
.transition-text {
position: absolute;
font-size: 12px;
font-weight: bold;
color: black;
transform-origin: 100% 50%;
display: none; /* Modificado */
}
.transition-legend {
position: absolute;
font-size: 12px;
font-weight: bold;
color: black;
transform-origin: 100% 50%;
}
</style>
</head>
<body>
<h1>Generador de AFD</h1>
<p>Ingrese los estados separados por coma (el <u>estado inicial</u> será el primero que ingreses y el <u>estado final</u> será el último) y las transiciones en el formato: inicio:valor:fin</p>
<p>Por ejemplo:</p>
<ol>
<li>Estados: A,B,C</li>
<li>Transiciones: A:a:B,B:b:C</li>
</ol>
<label for="estadosInput">Estados:</label>
<input type="text" id="estadosInput">
<button onclick="generarEstados()">Agregar Estados</button>
<label for="transicionesInput">Transiciones:</label>
<input type="text" id="transicionesInput">
<button onclick="generarTransiciones()">Agregar Transiciones</button>
<p>Estado inicial: <span id="estadoInicial"></span></p>
<p>Estado final: <span id="estadoFinal"></span></p>
<div id="afdGraph"></div>
<script>
const afdGraph = document.getElementById('afdGraph');
const states = new Map();
let estadoInicial;
let estadoFinal;
function generarEstados() {
const estadosInput = document.getElementById('estadosInput').value;
const nuevosEstados = estadosInput.split(',').map(state => state.trim());
nuevosEstados.forEach((state, index) => {
if (!states.has(state)) {
const isNewInitialState = index === 0 && !estadoInicial;
const isNewFinalState = index === nuevosEstados.length - 1 && !estadoFinal;
states.set(state, {
x: Math.floor(Math.random() * (afdGraph.offsetWidth - 40)),
y: Math.floor(Math.random() * (afdGraph.offsetHeight - 40))
});
const stateElement = document.createElement('div');
stateElement.className = 'estado';
stateElement.textContent = state;
stateElement.style.left = `${states.get(state).x}px`;
stateElement.style.top = `${states.get(state).y}px`;
if (isNewInitialState) {
stateElement.classList.add('inicial');
estadoInicial = state;
document.getElementById('estadoInicial').innerText = estadoInicial;
}
if (isNewFinalState) {
stateElement.classList.add('final');
estadoFinal = state;
document.getElementById('estadoFinal').innerText = estadoFinal;
}
stateElement.addEventListener('mousedown', startTransition);
stateElement.addEventListener('mouseup', stopTransition);
afdGraph.appendChild(stateElement);
}
});
}
let isCreatingTransition = false;
let transitionStart = null;
function startTransition(event) {
isCreatingTransition = true;
transitionStart = event.target;
}
function stopTransition(event) {
if (isCreatingTransition) {
const transitionEnd = event.target;
createTransition(transitionStart, transitionEnd);
isCreatingTransition = false;
transitionStart = null;
}
}
function createTransition(start, end) {
const startX = parseInt(start.style.left) + 20;
const startY = parseInt(start.style.top) + 20;
const endX = parseInt(end.style.left) + 20;
const endY = parseInt(end.style.top) + 20;
const transitionLine = document.createElement('div');
transitionLine.className = 'transition-line';
const length = Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2));
const angle = Math.atan2(endY - startY, endX - startX) * 180 / Math.PI;
transitionLine.style.width = `${length}px`;
transitionLine.style.left = `${startX}px`;
transitionLine.style.top = `${startY}px`;
transitionLine.style.transform = `rotate(${angle}deg)`;
afdGraph.appendChild(transitionLine);
const transitionLabel = document.createElement('div');
transitionLabel.className = 'transition-label';
transitionLabel.textContent = prompt('Ingresa el valor de la transición');
transitionLabel.style.left = `${(startX + endX) / 2}px`;
transitionLabel.style.top = `${(startY + endY) / 2}px`;
afdGraph.appendChild(transitionLabel);
const arrow = document.createElement('div');
arrow.className = 'transition-arrow';
arrow.style.left = `${endX}px`;
arrow.style.top = `${endY}px`;
const deltaX = endX - startX;
const deltaY = endY - startY;
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
const cosine = deltaX / distance;
const sine = deltaY / distance;
const arrowOffset = 20; // Ajuste el desplazamiento de la flecha aquí
arrow.style.transform = `translate(${-arrowOffset}px, -50%) rotate(${Math.acos(cosine) * (180 / Math.PI) * (sine < 0 ? -1 : 1)}deg)`;
afdGraph.appendChild(arrow);
const transitionText = document.createElement('div');
transitionText.className = 'transition-text';
transitionText.textContent = transitionLabel.textContent.split(':')[1]; // Extraer el valor de la transición
transitionText.style.left = `${(startX + endX) / 2}px`;
transitionText.style.top = `${(startY + endY) / 2}px`;
afdGraph.appendChild(transitionText);
const transitionLegend = document.createElement('div');
transitionLegend.className = 'transition-legend';
transitionLegend.textContent = transitionLabel.textContent.split(':')[0]; // Muestra el nombre de la transición
transitionLegend.style.left = `${(startX + endX) / 2}px`;
transitionLegend.style.top = `${(startY + endY) / 2}px`;
afdGraph.appendChild(transitionLegend);
}
function generarTransiciones() {
const transicionesInput = document.getElementById('transicionesInput').value;
const nuevasTransiciones = transicionesInput.split(',').map(transition => transition.trim());
nuevasTransiciones.forEach(transition => {
const [inicio, valor, fin] = transition.split(':').map(item => item.trim());
const start = [...afdGraph.querySelectorAll('.estado')].find(state => state.textContent === inicio);
const end = [...afdGraph.querySelectorAll('.estado')].find(state => state.textContent === fin);
if (start && end) {
createTransition(start, end);
}
});
}
</script>
</body>
</html>