Mostrando entradas con la etiqueta corewars. Mostrar todas las entradas
Mostrando entradas con la etiqueta corewars. Mostrar todas las entradas

viernes, 10 de diciembre de 2010

Concurso de programación de corewars

Me entero por Thoughts on Corewar que están organizando un concurso de programación para este juego. Traduzco el post:
El Primer Concurso Internacional de Programación de Corewars acaba de ser anunciado, con un primer premio de $50 patrocinado por www.corewar.info.

La competición tomará lugar en el área inexplorada entre la configuración '94nop y LP, que ha sido recientemente investigada por Fizmo en Redcode Talk.

La configuración exacta es como sigue:

Estándar:CWS'94
Tamaño del núcleo:8000
Procesos:200
Tamaño de la entrada:20
Puntos de victoria:3
Puntos de empate:1
Ciclos:80000
Rondas:1000

La línea de comandos de pMARS es pmars -p 200 -l 20 -r 1000. STP y LDP están prohibidos se permiten hasta dos entradas y las batallas serán en un torneo round-robin.


Para más información, hecha un vistazo al anuncio en rec.games.corewar.

Solo añadir que como dicen en la página web, el deadline es el 31 de diciembre.

Happy coding!

miércoles, 11 de agosto de 2010

Intérprete de Redcode

[Link corregido]

Hoy traigo un pequeño intérprete de Redcode (si, el lenguaje del Corewars).
Tiene sus limitaciónes (digamos que sigue las primeras versiones del lenguaje), por ejemplo, no tiene p-space ni modificadores (instrucción.Modificador) a excepcion de STS, que muestra un carácter por pantalla, y LDS que obtiene un carácter de STDIN (instrucciones que no se usan en el juego ), para más referencias [ Redcode - Esolang ].

Obviamente la idea al programar esto fue intentar comprender un poco más como funcionan los intérpretes y compiladores.

Centrandose en el programa en sí, (se puede descargar aquí [redcode01.tar.gz]), lo único que hay que hacer es make para compilarlo, y

./redcode <Código fuente>

para intérpretar un código.Además decir que ignora las líneas con un '#' al principio, para permitir que el intérprete se lance con un #!.

Hay más formas de usarlo, se pueden ver con

kenkeiras@viaxante:~/%%%%%$ ./redcode
Uso: ./redcode [--help |--compile | --decompile | --load-compiled] [--verbose] [--mem-length=<mem length>] <file>
--help: Muestra esto - Shows this
--compile: Compila el bytecode y lo muestra por STDOUT - Compiles the bytecode and shows it throught STDOUT
--decompile: Decompila el bytecode - Decompiles the bytecode
--load-compiled: Ejecuta un bytecode - Executes a bytecode
--verbose: Muestra información para el debugging - Shows debugging info
--mem-length: Ajusta la longitud de la memoria - Set's the mem length

Creo que es bastante auto explicativo.

La parte de compilar y cargar directamente despues está para evitar el procesamiento extra del parser (esto para Redcode no tiene mucho sentido, pero para otros lenguajes podria tenerlo,y recordemos que la idea era aprender =) ).

Por último, algunos programas utilizados para probarlo...

ps: Perdón desde ya por el surfing de punteros :S.

El hola mundo (no puede faltar :D) [http://pastebin.com/cbtGkj5F]:

; Redcode Hello World, John Metcalf

write   sts.a  hello,     0
        sts.b  }write,    0
        djn    write,     #7

hello   dat    72,        101 ; He
        dat    108,       108 ; ll
        dat    111,       44  ; o,
        dat    32,        87  ;  W
        dat    111,       114 ; or
        dat    108,       100 ; ld
        dat    33,        10  ; !\n


Adaptado para usar SPL (varios procesos)[http://pastebin.com/rqrugthE]:

 ; Redcode multiprocess Hello World
; Original by John Metcalf
; Modified by kenkeiras (Added parallel functions)

spl fork
write   sts.a  hello, 0
        djn    -1, #7   

hello   dat    72,        101 ; He
        dat    108,       108 ; ll
        dat    111,       44  ; o,
        dat    32,        87  ;  W
        dat    111,       114 ; or
        dat    108,       100 ; ld
        dat    33,        10  ; !\n

fork    sts.b  }write, 0
        djn    -1, #7



 Un imp (simple pero eficaz para probar los extremos de la memoria):

mov    0, 1


Y uno que se copia seguido hacia atras[http://pastebin.com/x8qNf15b]:

;name Backrunner
;author Kenkeiras

      SUB #7,inst    ;Para reajustar el puntero de instruccion
      SUB #6,pos     ; "
      MOV >inst,>pos
      SEQ inst,check
          JMP -2

      JMP -14 ;Al proximo paso

pos   DAT #-9
inst  DAT #0 ;Los valores que quedaran despues de copiarse a si mismos
check DAT #2


Hasta otra.

[Referencias]
http://impomatic.blogspot.com/
Redcode - Esolang

jueves, 10 de diciembre de 2009

Jugando a Core Wars [2° Parte/2]

Sigamos con el Core Wars...
Resumiendo, lo que vimos hasta ahora fue:

    ;Comentarios
    ;name nombre del programa
    ;author autor
    MOV A,B ;Copia A en B
    ADD A,B  ;Añade A a B
    JMP A     ;Se salta la instruccion A
    DAT A,B ;Un espacio para datos

Formas de indicar una direccion:

-Inmediata, por de la posicion donde esta el valor, se indica con un '#'
-Directa, por la posicion actual, se representa con '$' (o sin nada)
-Indirecta, por el valor de la posicion indicada, se indica con un '*' si es el primer campo (A), o con '@' si es el segundo campo (B)

Pero hay mas formas de indicar una direccion de forma indirecta:

- La del campo A, restandole 1 antes, '{'
- La del campo B, restandole 1 antes, '<'
- La del campo A, sumandole 1 despues, '}'
- La del campo B, sumandole 1 despues, '>'

Por ejemplo, se podria modificar el programa "dwarf"

            ;name Dwarf
            ;author A. K. Dewdney
dwarf  ADD #4, bomb
           MOV   bomb, @bomb
           JMP    dwarf

bomb  DAT #0


Haciendo que llene todos los espacios de memoria 1 a 1, en vez de cuatro en cuatro, y de paso eliminamos una instruccion (con lo que el programa es mas rapido)


dwarf  MOV   bomb, >bomb
          JMP    dwarf

bomb  DAT #1


Veamos las diferencias, la primera es que ya no esta la instruccion ADD por que lo que hacia (cambiar el valor de "bomb" para evitar estar siempre "golpeando la misma zona")ahora lo hace el MOV.
La segunda es el campo B de la instruccion MOV incrementa el valor del campo B de "bomb" uno de cada vez (por el '>').
La ultima es que el valor al que apunta "bomb" al principio es el siguiente ('#1'), ya que al no haber ADD y a que el incremento es posterior, si fuera el mismo ('#0'), se sobreescribiria, algo totalmente inutil.

Ahora algo mas complejo... aunque no por ello util:

      SUB #7,inst ;Para reajustar el puntero de instruccion
      SUB #6,pos ; Esto tambien
      MOV >inst,>pos
      SEQ inst,inst+1 ;Aqui esta el problema XD
      JMP -2
      JMP -14 ;Al proximo paso
pos  DAT #-9
inst  DAT #0 ;Los valores que quedaran despues de copiarse a si mismos
       DAT #2

Como se puede ver aqui hay instrucciones nuevas:
-La primera es SUB (de substract, restar ), funciona igual que el ADD, pero en vez de sumar, resta (logicamente es igual que hacer ADD de un numero negativo)
-La otra es SEQ (skip if equal, saltar si es igual), compara las dos instucciones y si son iguales se salta la siguiente instruccion

Veamos que hace el codigo entonces :
El DAT con la etiqueta pos, indica la posicion a donde se va a copiar, y el de la etiqueta inst, la instruccion que se va a copiar.
El DAT siguiente al de inst tiene siempre el valor #2, esto servira para comprobar si se acabo de copiar todo el codigo.

-Resta 7 al DAT que indica la instruccion a copiar (para que apunte al principio del codigo)    [ SUB #7,inst ]
-Resta 6 al DAT que indica donde se copiara la instruccion (para que apunte a la proxima seccion donde se copiara) [ SUB #6,pos ]
-Copia la instruccion a la que apunta inst a la posicion que indica pos, y incrementa en 1 los dos (para que apunten al siguiente) [ MOV >inst,>pos ]
-Si inst es igual a lo que hay en la siguiente posicions, se salta la siguiente posicion [ SEQ inst,inst+1 ]
-Vuelve atras 2 instrucciones [ JMP -2 ]
-Vuelve atras 14 instrucciones (pasa al bucle siguiente) [ JMP -14 ]

Es decir, que se copia todo hacia atras, hasta que acaba y salta hacia atras, para continuar copiandose.La utilidad que tiene esto es poca, con un minimo cambio en cualquier parte del codigo que se este ejecutando, el programa falla.

Las opciones para cambiar el flujo del programa son:

-SEQ (explicado anteriormente)
-SNE (funciona al reves que SEQ) (Skip if Not Equal, saltar si no es igual)
-SLT (se salta la siguiente instruccion si el campo A es menor que el campo B) (Skip if Less Than, saltar si es menos de)

-JNZ, si el valor del campo A es distinto de 0, se salta a la posicion que indica el campo B (Jump if Not Zero, saltar si no es cero)
-JZ, como JZN, pero si el valor de A es 0 (Jump if Zero, saltar si es cero)
-DJN, como JNZ, pero decrementa previamente el valor de A (Decrement & Jump if Not zero, decrementar y saltar si no es cero)

-SPL, se crea un segundo proceso que empieza en la posicion indicada (Split, separar)

La unica que introduce algo nuevo es SPL, veamos un ejemplo:

    SPL 0
    MOV 0,1

- SPL 0, crea un proceso mas, que seguira en la misma instruccion (si, asi continuamente)- MOV 0,1 ... un clasico imp (como el primero de todo)

Lo que hace es generar muchos procesos, ejecutando todos el codigo del imp
Si lo ejecutamos veremos que aunque al principio el conjunto de imps avanzan relativamente rapido, poco a poco van mas lentos, esto es por la forma que hay de repartir el tiempo entre los procesos:

Durante el turno de cada programa se ejecuta un proceso suyo y este no se volvera a ejecutar hasta que lo hayan hecho el resto

Esto significa que cuantos mas procesos haya para el programa, mas habra que esperar para que se repita el mismo proceso (por esto y por otras cosas suele haber un limite de procesos por programa)

Ya no se me ocurre nada mas que pueda hacer falta para escribir un programa de Core Wars, asi que eso es todo por ahora.

Quien no tenga problemas con el ingles tiene un buen tutorial en [ http://vyznev.net/corewar/guide.html ]

Actualizacion: Uno mas ( este no lo explicare :)}, es bastante sencillo )

    ;name Code Runner
    ;author Kenkeiras

    ADD #4,3
    SPL >2
    JMP -2
    DAT #1,#1

Suerte

domingo, 6 de diciembre de 2009

Jugando a Core Wars [1° Parte/2]

Aqui veremos como funciona el juego Core wars, en el que hay que escribir un programa que intente eliminar a sus oponentes (para esto hay varios metodos... hacer que dividan por cero, que intenten ejecutar algo que no sea codigo,etc )

No voy a explicar como instalarlo, ya que hay muchos programas que sirven para jugar, puedes encontrar muchos aqui: http://homepages.paradise.net.nz/~anton/cw/corewar-faq.html#CoreWarSystems
En los repositorios de Ubuntu/Debian hay un paquete llamado corewars [ sudo apt-get install corewars ] que sirve perfectamente, pero es posible que despues no este en el menu, si es asi tendras que abrir un terminal y lanzar corewars

El lenguaje que se usa (llamado Redcode) es bastante parecido al Ensamblador, asi que aunque quien maneje este tendra mucho trabajo adelantado (se usan ADD's, MOV's, JMP's...), para est@s ultim@s hay que destacar que la sintaxis es mas parecida a la de AT&T que a la de Intel (por ejemplo en el ADD el primer argumento es la cantidad a añadir y la segunda donde se añadira), se explicara todo como si no se tuviera ninguna nocion de ensamblador.

Nota: desde ya mismo perdon por algunas licencias que me tomare para intentar hacer mas sencillo todo (como llamar "bytes" a las posiciones de memoria)

Como primer ejemplo utilizaremos el programa mas sencillo, llamado imp (de impulso):

    ;name Imp
    ;author Walter Hofmann

    mov 0,1

Si lo ejecutamos veremos que va llenando la memoria byte a byte (si se le puede llamar asi), uno de cada vez.

Veamos como es el codigo... lo que va despues de un ';' es un comentario (no tiene relevancia en el juego), algunos MARS (las maquinas virtuales donde suceden los "combates") buscan cierta informacion en los comentarios, por ejemplo los que despues de el ';' llevan la palabra "name", especifican el nombre del programa, y los que despues del ';' llevan "author", muestran el nombre del autor del programa.

Hasta ahora no paso nada que afectase a la "maquina" que ejecuta el programa, ahora viene lo divertido,el codigo MOV 0,1 copia la instruccion (MOV) que esta en la posicion 0 desde el codigo (el propio codigo) hasta la posicion 1 desde el codigo, la posicion de memoria siguiente, con lo que la instruccion se copia a la siguiente posicion y despues se ejecuta lo que haya en la siguiente posicion ( si, el mismo codigo otra vez ).

Para entenderlo mejor veamos rapidamente como funciona la maquina:

La memoria de la maquina es circular (no hay punto inicial ni final), es lineal (solo se puede ir hacia adelante o hacia atras) y es relativa (solo se puede especificar una posicion a partir de la actual, no se puede decir por ejemplo, la posicion 74).

El procesador de la maquina ejecuta una instruccion ( que siempre ocupan 1 "byte" ) y a menos que la instruccion implique un cambio en el flujo del programa (JMP, JNZ... ya se explicaran despues) a continuacion se ejecuta la siguiente, si no hay ninguna o la operacion no esta permitida (como una division por 0), el proceso "muere".

Entonces nustro ejemplo pasaria algo asi:

t=0

 ...
 ---------------
 | ???          |
 ---------------
 | MOV 0,1  | <- (Se va a ejecutar esto)
 ---------------
 | ???          |
 ---------------
 | ???          |
 ---------------

...


t=1 (despues de ejecutar la primera instruccion)


 ...

 --------------
 | ???         |
 --------------
 | MOV 0,1 |
 --------------
 | MOV 0,1 | <- (Se va a ejecutar esto)
 --------------
 | ???         |
 --------------
...


t=2 (despues de la segunda instruccion)


 ...

 --------------
 | ???         |
 --------------
 | MOV 0,1 |
 --------------
 | MOV 0,1 |
 --------------
 | MOV 0,1 | <- (Se va a ejecutar esto)
 --------------
 | ???         |
 --------------
 ...


Asi el programa va "haciendo el camino" y sigue hasta que algo lo detenga.

Ahora veamos algo un poco mas complejo.

            ;name Dwarf
            ;author A. K. Dewdney
dwarf  ADD #4, bomb
           MOV   bomb, @bomb
           JMP    dwarf

bomb  DAT #0

Aqui se introducen muchas mas posibilidades, empezando por esta:

bomb   DAT #0

Esto significa, hay un "byte" que no contiene ninguna instruccion, sino datos, y a esta posicion tiene la etiqueta "bomb" (para no tener que ver donde seria su direccion con cada nueva instruccion), su uso es bastante simple, si alguien intenta ejecutarlo el proceso falla, y se puede utilizar para almacenar datos (de ahi su nombre, DAT).

La segunda es:

dwarf  ADD #4, bomb

Un "byte" con la etiqueta "dwarf" y con la instruccion ADD, que añade el valor del primer argumento (en este caso 4), a lo que este en la segunda posicion (en este caso a lo que tiene la etiqueta "bomb" ).

Despues tenemos:

    MOV   bomb, @bomb

Como ya vimos en el primer ejemplo, la instruccion MOV copia lo que haya en el primer valor, en el segundo,pero en este caso el segundo valor se utiliza para indicar donde se va a copiar...me explico:

Hay tres grupos de formas de indicar una direccion, inmediata, directa e indirecta.

La inmediata es la que se toma partiendo de la posicion de donde se toma el valor, se indica con un '#' antes del valor (por ejemplo, el DAT o el primer valor del ADD).

La directa es la que se calcula partiendo de la posicion actual, se indica con un '$' o no añadiendo nada (por ejemplo, en el primer valor del MOV o en el JMP).

La indirecta, es la que se calcula partiendo de la posicion que indique el "byte" que se indique, dependiendo de si es el que indica el primero (campo A) o el segundo argumento (campo B) del "byte" se indican con un '*' o '@' respectivamente (se pueden hacer mas cosas, pero ya me estoy extendiendo demasiado por ahora)

Entonces     MOV   bomb, @bomb     copia lo que haya en la posicion "bomb" a donde apunte el campo B de "bomb".
Por ultimo...

          JMP  dwarf

La instruccion JMP salta a la posicion que se indique, en este caso la posicion con la etiqueta dwarf (el principio del codigo).
En resumen, lo que hace el codigo es:

- Añade 4 a la posicion que apunta "bomb"  (MOV #4,bomb)
- Copia "bomb" a la posicion que apunta (MOV bomb,@bomb)
- Vuelve al principio (JMP dwarf)

Asi que va llenando la memoria de DAT's, (si algun programa los intenta ejecutar falla), por ejemplo, contra un imp, podria ocurrir esto.

t=0
...
 ---------------
 | MOV 0,1  |
 ---------------
 | MOV 0,1  | <- (Se va a ejecutar esto)
 ---------------
 | ???          |
 ---------------
 | ???          |
 ---------------
...

t=1 (cuando el imp se copia a la siguiente posicion, y en el mismo "turno" el "bomb" del programa anterior se copia a la misma posicion)

...                                                          ...
 ---------------                                       ----------------
 | MOV 0,1  |                                        | MOV 0,1  |
 ---------------                                       ----------------
 | MOV 0,1  |                                        | MOV 0,1  |
 ---------------                          =>         -----------------
 | MOV 0,1  | <-  (Se va a ejecutar)    | DAT         | <-
 ---------------                                       ------------------ 
 | ???          |                                        | ???          |
 ---------------                                       -----------------
...                                                        ...

Esto es todo por ahora... hay una guia bastante completa en [http://vyznev.net/corewar/guide.html]

Suerte