Universidad Interamericana de PanamáMateria: Compiladores Inicio Documentación Tema 6 · BYTE WARS
Documentación del Tema 6

BYTE WARS

Mini máquina virtual y bytecode · Universidad Interamericana de Panamá · Materia: Compiladores. Aquí se explica el lenguaje, el compilador, la máquina virtual, el juego, el proceso de trabajo y los resultados.

⬇ Descargar README completo Incluye lenguaje, compilador, bytecode, VM, juego, proceso y resultados.

1. Resumen

BYTE WARS es un juego 2D en el que el jugador no controla la nave con el teclado: escribe una estrategia en BWL (Byte Wars Language), el compilador la traduce a bytecode y una máquina virtual ejecuta cada instrucción para mover, disparar y activar el escudo.

El núcleo completo está escrito en C99 y se compila con Emscripten a WebAssembly, por lo que corre directamente en el navegador. El mismo núcleo se compila también a un ejecutable nativo de consola para pruebas y demostración.

código BWL
   │
   ▼
lexer → parser → AST → análisis semántico → bytecode
   │
   ▼
máquina virtual (PC, pila, variables, saltos)
   │
   ▼
juego 2D (naves, balas, escudo, salud, puntuación)
El objetivo académico es demostrar el ciclo completo de un compilador y una VM reales: no un bytecode decorativo, sino el mecanismo que controla el juego.

2. Equipo

CZCarlos Zachrisson
KAKaren Arauz

3. Proceso

El trabajo se organizó en fases con entregables verificables. Cada fase terminó con compilación y pruebas antes de pasar a la siguiente.

FaseActividadResultado
0Análisis del portal existente, copia de seguridad y commit inicialLínea base protegida; Temas 11 y 4 conservados en legacy/
1Especificación de BWL, lexer y parserTokens, AST, errores léxicos y sintácticos con línea/columna
2Análisis semántico, tabla de símbolos y generación de bytecodeVariables, builtins, validaciones, desensamblador
3VM de pila, juego determinista y CLI nativoEjecución paso a paso, bucles, WAIT, colisiones y oleadas
4Puente WebAssembly con Emscriptenbytewars.wasm de 87 KB y bytewars.js
5Interfaz web: editor, paneles, canvas y controlesTablero de cuatro paneles y análisis del compilador
6Documentación, capturas y resultadosEsta página, README y documentos de equipo/proceso/resultados
7QA final, responsive, accesibilidad y DockerCompletada: Chromium headless, móvil 390 px, Docker build y contenedor verificados

Decisiones técnicas

  • Núcleo en C99: es el requisito del tema y permite el mismo código en WASM y en nativo.
  • Ejecución en el cliente: el navegador compila y ejecuta; el servidor no ejecuta código del usuario.
  • VM de pila: las operaciones y comparaciones son sencillas de explicar y de visualizar.
  • Arenas estáticas y límites: evitan fugas de memoria y hacen el comportamiento predecible.
  • PRNG xorshift con semilla fija: las partidas y las pruebas son reproducibles.
  • Sin assets externos: naves, balas y fondo se dibujan con Canvas 2D.
  • Juego a pantalla completa (PC y móvil): al ejecutar, el juego se abre sobre la página con HUD, PC/ticks y controles, y una cuenta regresiva 3 · 2 · 1 antes de empezar. En móvil, un botón flotante permite reabrirlo sin hacer scroll.
  • Tema claro/oscuro: variables CSS, detección de prefers-color-scheme, botón sol/luna y persistencia. El canvas del juego se mantiene oscuro para conservar el contraste.

Dificultades y soluciones

  • Las constantes decimales no caben en un operando entero: se añadió un campo double num a la instrucción PUSH.
  • Un bucle infinito podía congelar la página: la VM tiene un presupuesto de 200 instrucciones por tick y cede el turno.
  • La VM y el juego no debían quedar acoplados: la VM recibe un BwHost con callbacks, lo que permite probarla con un host falso.
  • El portal existente no debía romperse: los Temas 11 y 4 se conservaron y se ocultaron del menú principal para la entrega del Tema 6.

4. Resultados

La aplicación permite escribir, compilar, inspeccionar y ejecutar un programa completo. El tablero muestra los cuatro paneles conectados en tiempo real.

Tablero de BYTE WARS con editor, bytecode, VM y juego

Qué funciona

  • Compilación con errores de léxico, sintáctico y semántico, con línea y columna.
  • Bytecode visible, instrucción actual resaltada y desensamblador con direcciones.
  • VM con PC, pila, variables reservadas y de usuario, ticks y estado.
  • Modo tiempo real y modo paso a paso.
  • Movimiento, disparo, escudo, espera, bucles, condicionales y builtins.
  • Juego con salud, puntuación, oleadas, enemigos que disparan y victoria/derrota.
  • Protección contra bucles infinitos y errores de ejecución.
  • Descarga de bytewars.exe y bytewars_cli.
Panel de la VM
PC, pila, variables y estado de la VM.
Juego 2D
Nave, enemigos, escudo y HUD.
Pantalla de victoria de BYTE WARS
Victoria: la VM y el juego comparten el estado; la puntuación y la salud finales son las del frame real.
BYTE WARS en un móvil de 390 px
Vista móvil a 390 px: una columna, controles apilados y sin overflow horizontal.
Juego a pantalla completa en móvil
Al ejecutar, el juego se abre a pantalla completa con HUD, PC/ticks y controles. No hace falta hacer scroll.
Cuenta regresiva 3 2 1
Cuenta regresiva 3 · 2 · 1 antes de iniciar la partida, tanto en PC como en móvil.
Tablero de BYTE WARS en modo claro
Tema claro en escritorio: paneles, editor y tablas legibles; el canvas conserva el espacio oscuro.
Juego a pantalla completa en modo claro en móvil
Pantalla completa en móvil con tema claro; el botón sol/luna cambia de tema y la preferencia se guarda.

Métricas medidas

MétricaResultado
Tiempo de compilación del ejemplo (21 instrucciones)≈ 0.2 ms en el navegador
Serialización de un frame de juego≈ 0.03 ms
Tamaño de bytewars.wasm87 KB
Tamaño de bytewars_cli / bytewars.exe69 KB / 98 KB
Pruebas unitarias en C16 grupos, todos pasando
Límite de instrucciones por tick200, con cesión de turno

Limitaciones conocidas

  • Un nivel de tres oleadas; el selector de nivel ajusta la dificultad, no añade mapas nuevos.
  • Sin sonido ni multijugador.
  • bytewars.exe es un runner de consola, no una ventana gráfica nativa.
  • La VM es didáctica; no emula hardware real.

5. Lenguaje BWL

BWL es un lenguaje imperativo pequeño, con bloques, expresiones aritméticas y lógicas, variables y llamadas a funciones integradas.

// Estrategia básica
START
SET speed 3
REPEAT 5 {
  MOVE RIGHT
  SHOOT
  WAIT 10
}
WHILE enemy_ahead() {
  IF health < 30 {
    SHIELD
  } ELSE {
    SHOOT
    MOVE UP
  }
}
END

Gramática

programa       := sentencia*
sentencia      := 'START' | 'END'
                | 'SET' IDENT expresion
                | 'MOVE' ('UP'|'DOWN'|'LEFT'|'RIGHT')
                | 'SHOOT' | 'SHIELD'
                | 'WAIT' expresion
                | 'REPEAT' expresion bloque
                | 'WHILE' expresion bloque
                | 'IF' expresion bloque ('ELSE' bloque)?
bloque         := '{' sentencia* '}'
expresion      := or
or             := and ('||' and)*
and            := igualdad ('&&' igualdad)*
igualdad       := comparacion (('=='|'!=') comparacion)*
comparacion    := suma (('<'|'>'|'<='|'>=') suma)*
suma           := multiplicacion (('+'|'-') multiplicacion)*
multiplicacion := unario (('*'|'/'|'%') unario)*
unario         := ('-'|'!') unario | primario
primario       := NUMERO | IDENT | IDENT '(' argumentos? ')'
                | '(' expresion ')' | 'TRUE' | 'FALSE'

Variables reservadas

NombreTipoSignificado
health0–100Salud de la nave
scorenúmeroPuntuación
enemies_leftnúmeroEnemigos vivos
levelnúmeroNivel actual
shield0/1Escudo activo
ticksnúmeroTicks de juego

Las variables del usuario se crean con SET y deben existir antes de leerse. Los nombres reservados son de solo lectura.

Funciones integradas

FunciónArgumentosDevuelve
enemy_ahead()01 si hay un enemigo delante
enemy_left()01 si hay un enemigo a la izquierda
enemy_right()01 si hay un enemigo a la derecha
random(n)1Entero pseudoaleatorio entre 0 y n-1

Límites

  • Fuente: 16 KB; tokens: 2048; nodos del AST: 2048; instrucciones: 2048; variables: 64; pila: 256.
  • Profundidad de bloques/expresiones: 64.
  • WAIT: 0–600 ticks. REPEAT: hasta 10 000 iteraciones por entrada.
  • 200 instrucciones de VM por tick de juego para evitar bloqueos.

6. Compilador

El compilador está en bytewars/src/ y se organiza en fases separadas.

ArchivoResponsabilidad
lexer.cConvierte el texto en tokens con línea y columna; ignora comentarios //.
parser.cDescenso recursivo; construye el AST y reporta errores sintácticos.
sema.cTabla de símbolos, variables reservadas, builtins, aridad y división entre cero literal.
codegen.cAST → bytecode, parcheo de saltos y desensamblador.
compiler.cOrquesta las fases y conserva los diagnósticos.

Ejemplo de AST

Programa
  START
  SET speed
    NUM 3
  REPEAT
    NUM 5
    BLOQUE
      MOVE RIGHT
      SHOOT
      WAIT
        NUM 10
  WHILE
    BINARIO >
      VAR health
      NUM 0
    BLOQUE
      IF
        CALL enemy_ahead (0 args)
        BLOQUE
          SHOOT
      ELSE
        BLOQUE
          WAIT
            NUM 3
  END

Errores por fase

FaseEjemplo
LéxicoSET x @ → «Carácter no reconocido»
SintácticoFalta } → «Se esperaba '}'…»
SemánticoSET x y → «Variable no definida 'y'»; SET health 5 → variable reservada
EjecuciónDivisión entre cero dinámica, pila vacía, salto inválido

7. Bytecode e ISA

Cada instrucción tiene un opcode, un operando entero, la línea del fuente y, para PUSH, una constante decimal.

typedef struct {
    uint8_t op;
    int32_t arg;      // variable, dirección o builtin
    int16_t line;     // línea del fuente
    double  num;      // constante de PUSH
} BwInstr;
OpcodeOperandoPilaDescripción
PUSHnúmero+1Empuja una constante
LOAD / STOREvariable+1 / −1Lee o escribe una variable
ADD SUB MUL DIV MOD—−1Aritmética
NEG NOT—0Negación aritmética o lógica
LT GT LE GE EQ NE—−1Comparaciones (1 o 0)
AND OR—−1Lógica (evaluación no cortocircuitada)
JMP JZ JNZdirección0 / −1Saltos
CALLbuiltinvariableLlama a una función integrada
MOVE_UP/DOWN/LEFT/RIGHT—0Mueve la nave
SHOOT SHIELD—0Acciones de combate
WAIT—−1Espera n ticks
REPEAT_START / REPEAT_ENDdirección−1 / 0Bucle contado con pila de bucles
HALT—0Detiene la VM

Desensamblado del ejemplo

0000  PUSH           3
0001  STORE          speed
0002  PUSH           5
0003  REPEAT_START   0009
0004  MOVE_RIGHT
0005  SHOOT
0006  PUSH           10
0007  WAIT
0008  REPEAT_END
0009  LOAD           health
0010  PUSH           0
0011  GT
0012  JZ             0020
0013  CALL           enemy_ahead
0014  JZ             0017
0015  SHOOT
0016  JMP            0019
0017  PUSH           3
0018  WAIT
0019  JMP            0009
0020  HALT

8. Máquina virtual

La VM es de pila y mantiene todo el estado visible en la interfaz.

typedef struct {
    BwInstr code[2048]; int code_len;
    BwVar   vars[64];   int var_count;
    double  stack[256]; int sp;
    int pc;
    BwLoopFrame loops[32]; int loop_sp;
    int wait_ticks, halt_ticks, status;
    long steps, ticks;
    int budget_hits, has_error;
    BwDiag error;
    BwHost host;
} BwVM;
  • Ejecución: cada tick de juego ejecuta hasta 200 instrucciones o hasta WAIT/HALT.
  • Espera: WAIT n deja la VM esperando n ticks.
  • Bucles: REPEAT_START guarda el contador en una pila de bucles; REPEAT_END lo decrementa.
  • Saltos: JZ/JNZ validan que la dirección exista.
  • Errores: pila vacía, división entre cero, variable inválida, salto inválido, opcode desconocido, presupuesto excedido.
  • Reinicio: al llegar a HALT/END, el programa se reinicia tras 30 ticks para seguir controlando la nave.
La VM no conoce el juego directamente: recibe un BwHost con callbacks para mover, disparar, activar el escudo y consultar builtins. Eso permite probarla con un host falso.

9. Juego

Reglas

  • Área lógica de 480 × 640.
  • La nave se mueve en la parte inferior; los límites se respetan.
  • Disparo con recarga; escudo temporal con recarga.
  • Enemigos exploradores, cazadores y tanques.
  • Salud 100; daño por impactos y por enemigos que cruzan la línea.
  • Victoria al limpiar tres oleadas; derrota si la salud llega a 0.

Determinismo

Todo el azar usa un PRNG xorshift32 con semilla fija. El mismo programa, nivel y semilla producen el mismo resultado, lo que permite pruebas automáticas de colisiones, victoria y derrota sin depender del tiempo real.

El selector de nivel 1–3 ajusta velocidad, tipos de enemigo y frecuencia de disparo.

10. Arquitectura y tecnologías

Navegador
  tema6.html + bytewars.js
    ├── editor, paneles, canvas
    └── ByteWarsModule (WebAssembly)
          ├── lexer.c → parser.c → sema.c → codegen.c
          ├── vm.c
          └── game.c → json.c
Servidor Node
  ├── sirve public/
  ├── /builds/ (proyecto anterior, intacto)
  └── scripts/build-bytewars.sh → public/wasm
Nativo
  ├── bytewars_cli (Linux)
  └── bytewars.exe (Windows, consola)
C99Compilador, VM y juego
EmscriptenC → WebAssembly
WebAssemblyEjecución en el navegador
HTML/CSS/JSEditor, paneles y controles
Canvas 2DNaves, balas y fondo procedural
mingw-w64Runner .exe de consola

11. Cómo ejecutar

Requisitos: Node 18+, gcc, Emscripten y, opcionalmente, mingw-w64 para el .exe.

# 1. Construir el núcleo (WASM + binarios nativos)
npm run build:bytewars

# 2. Arrancar el portal
npm start
#    http://localhost:3000

# 3. Pruebas
npm test

# 4. Solo el núcleo C
make -C bytewars test
make -C bytewars cli

Si falta el WASM, npm start intenta construirlo automáticamente. La página debe servirse por HTTP, no abrirse con file://, porque el navegador bloquea la carga del módulo WebAssembly.

Docker

docker compose up -d --build

La imagen instala Node, gcc, mingw-w64 y Emscripten, y compila el WASM durante el build. El contenedor escucha en el puerto 3000 y se publica con Traefik.

12. Pruebas

El proyecto incluye pruebas unitarias en C y pruebas de integración con el CLI.

== Pruebas de BYTE WARS ==
lexer
parser
errores por fase
codegen
vm aritmética
vm if/else
vm repeat/while
vm error de ejecución
vm wait
vm acciones y builtins
vm límite por tick
juego límites
juego disparo/escudo
juego determinista
programa de ejemplo
json

Todas las pruebas pasaron.
NivelQué comprueba
LexerPalabras clave, identificadores, números, comentarios y errores.
ParserBloques, precedencia, IF/ELSE, REPEAT/WHILE y errores.
SemánticaVariables no definidas, reservadas, builtins, aridad y división literal.
CodegenInstrucciones, saltos parcheados, desensamblador y límites.
VMAritmética, ramas, bucles, WAIT, builtins, errores y presupuesto.
JuegoLímites, disparo, escudo, determinismo y colisiones.
IntegraciónLos cuatro ejemplos compilan; el ejemplo con errores falla en sintáctico; el frame contiene VM y juego.
NavegadorCarga del WASM, compilación, ejecución, pausa, paso a paso y variables en vivo.

13. Descargas

Runner nativo

Ejecuta programas BWL desde la consola con el mismo núcleo C. Si se abre con doble clic, corre una demo incluida; desde la consola: bytewars.exe programa.bwl.

El código fuente completo está en bytewars/. Los Temas 11 y 4 se conservan en legacy/ y en sus páginas originales, fuera del menú principal.

14. Conclusiones

BYTE WARS demuestra que un compilador y una máquina virtual escritos en C pueden ser el centro de un juego real y ejecutarse en el navegador sin instalar nada. El jugador ve el ciclo completo: escribe una estrategia, observa los tokens y el AST, revisa el bytecode, sigue la ejecución de la VM y comprueba el efecto en la nave.

El proyecto separa claramente las responsabilidades: el compilador no dibuja, la VM no conoce el juego y la interfaz solo consume snapshots JSON. Esa separación permitió probar cada parte por separado y mantener el juego determinista.

Referencias

  • Notas de la materia de Compiladores: análisis léxico, sintáctico, semántico y generación de código.
  • Documentación de Emscripten: emcc, MODULARIZE y EXPORTED_FUNCTIONS.
  • Especificación de WebAssembly y Canvas 2D del W3C.
  • Documentación de Node.js para el servidor HTTP.