Los operadores por familia
Panorama de los operadores del ADN agrupados por familia: qué toman y qué dejan en cada pila, y los más usados con un ejemplo.
El ADN se escribe en notación polaca inversa: primero van los datos y después el operador que los usa. 3 4 add apila un 3, apila un 4, y add los saca a los dos y deja un 7. No hay paréntesis ni precedencia: el orden de las palabras es el orden de las cuentas.
Los operadores trabajan sobre dos pilas, la de enteros y la booleana (las explica La pila entera y la booleana). Según qué pila tocan y qué hacen, se agrupan en familias. Cada familia tiene su página en la referencia, con la tabla completa de sus operadores y su costo:
| Familia | Toma de | Deja en | Para qué |
|---|---|---|---|
| Básicos | enteros | enteros | aritmética y manejo de la pila |
| Avanzados | enteros | enteros | ángulos, distancias, raíces, trigonometría |
| Bit a bit | enteros | enteros | operar con los bits de un número |
| Comparaciones | enteros | booleana | preguntar (mayor, igual, parecido…) |
| Lógicos | booleana | booleana | combinar respuestas (y, o, no) |
| Escritura en memoria | enteros | memoria | guardar valores y dar órdenes |
| Flujo | — | — | marcar dónde empieza y termina cada gen |
Además están los números sueltos y las lecturas de memoria como *.eye5, que solo apilan un valor: los cuenta Números y lecturas y los explica Números y direcciones.
Cómo leer las descripciones
En esta página la pila se escribe a b, con b arriba de todo (el último que entró). Así, a b sub calcula a − b: 10 3 sub da 7.
Ningún operador falla nunca. Si la pila de enteros está vacía, sacar de ella da 0; si la booleana está vacía, cuenta como verdadero. Cada pila tiene 101 lugares; si se llena, el valor más viejo se pierde sin aviso. Un operador mal usado no rompe el bot: hace algo, aunque no sea lo que querías.
Los operadores de las cinco primeras familias corren dentro de un gen, tanto en la condición (después de cond) como en el cuerpo (después de start o else). Fuera de un gen, por ejemplo antes del primer cond, no hacen nada. Los de escritura, en cambio, solo corren en el cuerpo.
Básicos
Son la aritmética y las herramientas para acomodar la pila. Todos sacan sus operandos de la pila de enteros y dejan el resultado ahí mismo.
- Cuentas:
add(suma),sub(resta),mult(producto),div(división),mod(resto),abs(valor absoluto),sgn(signo: −1, 0 o 1) yrnd(un número al azar entre 0 y el tope, ambos incluidos). - Manejo de la pila:
dupduplica el tope,droplo tira,swapintercambia los dos de arriba,overcopia el segundo arriba de todo (a b → a b a) yclearvacía la pila. *lee la memoria en la dirección que está en el tope:740 *es lo mismo que*740, pero la dirección se puede calcular.
Algunas rarezas que conviene saber, porque así se comportaba el 2.48.32:
divno trunca: redondea al entero más cercano y, en el empate, al par.7 2 divda 4,5 2 divda 2 y100 7 divda 14. Dividir por cero da 0.modconserva el signo del dividendo:-7 3 modda −1 y7 -3 modda 1.dupsobre la pila vacía apila dos ceros.
Un ejemplo con rnd y mod: el bot cambia de rumbo al azar cada 20 ciclos de vida (.robage) y siempre avanza. Un giro completo son 1256 unidades de ángulo, así que 1256 rnd es una dirección cualquiera.
' Cambia de rumbo al azar cada 20 ciclos y avanza
cond
*.robage 20 mod 0 =
start
1256 rnd .setaim store
stop
cond
start
10 .up store
stopAvanzados
Cuentas más elaboradas, casi todas pensadas para moverse por el mundo:
angletoma un puntox yy da el ángulo desde el bot hasta ese punto, en las mismas unidades que.aim(0 a 1256).distda la distancia hasta el punto.anglecmpcompara dos ángulos y da la diferencia con signo, entre −628 y 628: cuánto hay que girar, y el signo dice hacia qué lado.sinycostoman un ángulo en esas unidades y devuelven el seno o el coseno multiplicado por 32000:314 sin(un cuarto de vuelta) da 32000.sqr(raíz cuadrada; 0 si el número no es positivo),pow(potencia:2 10 powda 1024; el exponente se limita a ±10),root(27 3 rootda 3),logx(logaritmo en baseb:1000 10 logxda 3) ypyth(3 4 pythda 5, la hipotenusa).ceilpone un techo yfloorun piso:x 100 ceildejaxpero nunca más de 100, yx 0 floorlo deja pero nunca menos de 0.debugintydebugbooldejan una traza para depurar, que muestra el comandodebugde la consola del El inspector. No cobran energía y casi nunca cambian la pila (debugboolsobre la pila booleana vacía apila un verdadero).
El bot 4-d_Swarmer del Bestiario usa angle con .refxpos y .refypos (la posición de lo que está viendo) para girar hacia una planta: *.refxpos *.refypos angle .setaim store. Con un punto fijo se ve igual de bien: este bot apunta hacia (3000, 2500) y avanza hacia allá (si no frena, se pasa de largo).
' Apunta al punto (3000, 2500) y avanza hacia él
cond
start
3000 2500 angle .setaim store
10 .up store
stopBit a bit
Tratan el número como 32 bits. &, | y ^ hacen el y, el o y el o exclusivo bit a bit de los dos del tope; ~ invierte todos los bits; << y >> corren los bits un lugar (multiplican o dividen por 2, y >> conserva el signo: -5 >> da −3); ++ y -- suman o restan 1.
Sirven para guardar varias marcas en una sola celda. *740 4 | 740 store prende el bit de valor 4 en la celda 740, y *740 4 & da 4 si está prendido o 0 si no.
Comparaciones
Son las preguntas del ADN. Cada una saca enteros de la pila de enteros y deja un resultado, verdadero o falso, en la booleana. Con a b en la pila:
| Operador | Es verdadero si… |
|---|---|
< · > | a es menor · mayor que b |
<= · >= | a es menor o igual · mayor o igual que b |
= · != | a es igual · distinto de b |
%= · !%= | b está · no está dentro del 10 % de a |
~= · !~= | con a b d: b está · no está dentro del d % de a |
100 105 %= es verdadero y 100 115 %= es falso; 100 115 20 ~= es verdadero. Una rareza heredada: si a es negativo, %= y ~= dan siempre falso.
En la condición de un gen se pueden poner varias comparaciones seguidas: al llegar a start se exige que todas sean verdaderas. Este bot avanza solo mientras tiene más de 1000 de energía (.nrg):
' Avanza mientras tenga más de 1000 de energía
cond
*.nrg 1000 >
start
10 .up store
stopUna comparación también puede ir en el cuerpo, y entonces decide si se ejecutan las escrituras que la siguen; eso es Condiciones en línea.
Lógicos
Trabajan solo sobre la pila booleana. and, or y xor combinan los dos resultados de arriba; not invierte el tope; true y false apilan una constante. Y la pila booleana tiene su propio juego de herramientas: dupbool, dropbool, swapbool, overbool y clearbool.
Como el start ya une todas las condiciones con un y, el or es el que más se escribe: sin él no hay forma directa de pedir «esto o aquello».
' Avanza si la celda 740 vale 0 o si la 741 vale 0
cond
*740 0 =
*741 0 =
or
start
10 .up store
stopSi falta un operando, cuenta como verdadero (la pila booleana vacía vale verdadero). Por eso not sobre la pila vacía da falso, y un cond start sin ninguna condición ejecuta su cuerpo siempre.
Escritura en memoria
Son los únicos que cambian algo fuera de las pilas: escriben en la memoria del bot, y escribir en ciertas direcciones es dar una orden (moverse, disparar, reproducirse). El más usado es store: 10 .up store guarda 10 en la dirección de .up y el bot avanza. Hay variantes que operan sobre lo que ya hay en la celda: inc y dec le suman o restan 1, addstore le suma un valor, negstore le cambia el signo, y así hasta catorce.
Solo corren en el cuerpo de un gen y solo si el tope de la pila booleana es verdadero (o está vacía). La dirección 0 no escribe nada. Todos los detalles, incluido cómo se recorta el valor escrito, están en Escribir en la memoria.
Flujo
No calculan nada: marcan la estructura. cond abre un gen y su condición, start abre el cuerpo que corre si la condición dio verdadero, else el que corre si dio falso, y stop cierra el gen. end termina la lectura del ADN; el cargador lo agrega solo al final, así que no hace falta escribirlo. Cómo se combinan está en Genes: cond, start, else y stop.
A diferencia del resto, estos marcadores se ejecutan siempre, incluso en un gen que se está salteando, y cada uno cobra su costo.
Un truco del Bestiario: cuentas en lugar de condiciones
Algunos autores escriben bots enteros sin condiciones, con aritmética. El truco se apoya en que dividir por cero da 0: x dup div vale 1 si x no es cero y 0 si lo es. Y x dup div 1 sub abs es al revés: 1 si x es cero. Multiplicando por eso se «prende» o «apaga» un valor. Bardus, de Moonfisher, lleva la idea al extremo: todo el bot es un único store de una línea larguísima.
Una versión chica: avanzar 10 si el ojo central (.eye5) no ve nada, y quedarse quieto si ve algo.
' Avanza solo si el ojo central no ve nada, sin condiciones
cond
start
*.eye5 dup div 1 sub abs 10 mult .up store
stopSe lee peor que la forma con cond, así que para empezar conviene esa. Lo que cuesta cada operador se cuenta en Ejecución y costos.