DarwinBotsManual
Abrir la app EnglishEN

La pila entera y la booleana

El ADN calcula con dos pilas, una de números y otra de verdadero o falso: cuánto les cabe, qué pasa si se llenan o se vacían y cuándo arrancan de cero.

El ADN de DarwinBots es un lenguaje de pila: no hay variables ni paréntesis, cada palabra deja algo en una pila o lo saca de ahí. Si alguna vez usaste una calculadora RPN, ya conocés la idea: primero los operandos, después la operación.

Hay dos pilas, y cada palabra sabe con cuál trabaja:

PilaGuardaLa llenanLa vacían
Enteranúmerosnúmeros sueltos, lecturas como *.nrg, operadores como addoperadores, condiciones y stores
Booleanaverdadero o falsocondiciones como >, true, falselógicos como and, el start de un gen, dropbool

La pila entera

Un número escrito en el ADN se apila. Un operador saca los que necesita y apila el resultado. Un store saca una dirección y un valor y escribe en la memoria del bot. Este bot suma 3 y 4 y guarda el 7 en la dirección 50, una celda de memoria libre:

cond
start
  3 4 add 50 store
stop

Paso a paso, la pila entera queda así (el tope a la derecha):

PalabraPila después
33
43 4
add7
507 50
store(vacía): escribió 7 en la dirección 50

El orden importa en las operaciones que no son conmutativas: 10 3 sub es 10 − 3, y *.nrg 1000 > pregunta si la energía es mayor que 1000. La regla es siempre la misma: el que está más abajo es el primer operando. Por eso un store se escribe valor dirección store, con la dirección arriba.

En la pila entera caben números mucho más grandes que en la memoria (hasta unos dos mil millones); el recorte a ±32000 pasa recién al guardar. Lo contamos en Números y direcciones.

La pila booleana

Las condiciones sacan dos números de la pila entera y apilan un resultado en la booleana. *.nrg 1000 > saca la energía y el 1000 y apila verdadero o falso. Los lógicos (and, or, not, xor) trabajan solo sobre la booleana.

Varias condiciones seguidas entre cond y start se combinan con un y implícito: el start hace el y de todo lo que haya en la pila booleana y la deja vacía. Para un o hay que escribirlo. En este bot la primera condición es cierta y de las otras dos solo una lo es; con el or el gen se ejecuta y escribe 9 en la dirección 57:

cond
  1 1 =
  2 2 = 3 4 = or
start
  9 57 store
stop

Todo lo de las condiciones del gen está en Genes: cond, start, else y stop, y los operadores uno por uno en Los operadores por familia.

Tamaño: 101 lugares

Cada pila tiene lugar para 101 valores. Si apilás uno más, la pila no da error: se pierde el valor más viejo, el del fondo, y el nuevo entra arriba.

Lo comprobamos apilando un 1000 y después 101 unos: al sumar todo, el resultado es 101 y no 1101, porque el 1000 del fondo se cayó al entrar el último 1. En la práctica no vas a llegar a 101 salvo con un error (un número que se apila en cada vuelta y nadie saca), pero conviene saber que no avisa.

Sacar de una pila vacía

Tampoco hay error al sacar de una pila vacía. Cada pila tiene su regla:

Por ejemplo, este bot no apila nada antes del store: la dirección 52 la saca bien, pero el valor sale de la pila vacía y vale 0. Si la dirección 52 tenía otro número, queda en 0:

cond
start
  52 store
stop

Y por la regla de vacía = verdadero:

Algunos operadores de pila tienen asimetrías heredadas del DarwinBots 2.48.32 que el port respeta tal cual:

OperadorCon la pila vacíaCon un solo valor
dupapila dos ceroslo duplica
dupboolno hace nadalo duplica
overno hace nadaapila un 0 encima
overboolno hace nadaapila verdadero encima
swap, swapboolno hacen nadano hacen nada

Cuándo se vacían

Al empezar el turno de cada bot, en cada ciclo, las dos pilas arrancan vacías. Nada pasa de un ciclo al siguiente por las pilas: lo que quieras recordar lo tenés que guardar en memoria (ver Memoria libre y epigenética). Este bot deja un 7 en la pila al final de su turno; en el ciclo siguiente el store no lo encuentra y escribe 0 en la dirección 50:

cond
start
  50 store
  7
stop

Dentro de un mismo ciclo, en cambio, la pila entera no se vacía entre genes. Un número que sobra en un gen lo puede usar el siguiente. Acá el primer gen apila un 7 y el segundo lo guarda en la dirección 51:

start
  7
stop

start
  51 store
stop

Funciona, pero hace al bot frágil: si el primer gen no se ejecuta, el segundo encuentra otra cosa. Mejor que cada gen deje la pila como la encontró.

La pila booleana tiene dos momentos de limpieza:

Una rareza: el booleano que sobrevive

Un gen que empieza con start directo (sin cond) no limpia la pila booleana. Si el gen anterior dejó un falso arriba, ese falso bloquea los stores del gen nuevo, aunque el gen se ejecute. En este bot la dirección 51 nunca se escribe:

start
  1 2 = 3 50 store
stop

start
  4 51 store
stop

Basta abrir el segundo gen con cond para que la pila booleana arranque limpia y el 4 se guarde. El falso sigue mandando en todos los genes que vengan después, hasta el próximo cond. Es un comportamiento del DarwinBots original que el port conserva para que los bots viejos se comporten igual. Si escribís genes con condiciones en el cuerpo, abrí cada gen con cond, o terminá el cuerpo con clearbool.

Condiciones dentro del cuerpo

Una condición también se puede evaluar entre start y stop. No decide si el gen corre, pero el tope de la pila booleana gobierna todos los stores que siguen: si es falso, no escriben; si es verdadero o la pila está vacía, sí. El store mira el tope sin sacarlo, así que el mismo resultado sigue mandando hasta que lo saques con dropbool, lo tapes con otra condición o vacíes la pila con clearbool.

cond
start
  1 2 > 7 53 store
  dropbool
  8 54 store
stop

Como 1 no es mayor que 2, el 7 no se guarda en la 53; después del dropbool la pila queda vacía (verdadero) y el 8 sí se guarda en la 54. Ojo: la condición solo frena a los stores. Los números y operadores del cuerpo se ejecutan igual.

El bot «Alga_Pair 1.1.1» del Bestiario usa esta técnica en una línea: cuenta una generación más solo en el ciclo en que nace (cuando .robage vale 0) y enseguida descarta la condición para que no frene al resto del gen:

def generation 971

cond
start
  .generation *.robage 0 = inc dropbool
stop

Fijate que inc también es un store, y por eso la condición lo frena. La técnica completa, con más ejemplos, está en Condiciones en línea.

Resumen

Pila enteraPila booleana
Tamaño101 valores101 valores
Al desbordarse pierde el más viejose pierde el más viejo
Sacar estando vacíada 0cuenta como verdadero
Se vacíaal empezar el turno del botal empezar el turno, en cada cond y al decidir el start
Pasa de un gen al otrosí, dentro del mismo ciclosolo si el gen siguiente no abre con cond
Pasa al ciclo siguientenono