tanks/evolve.go

195 lines
3.2 KiB
Go

package main
import (
"fmt"
"math/rand"
"strconv"
"time"
"strings"
"io/ioutil"
"encoding/json"
)
type sensor struct {
srange int
angle int
width int
turret bool
}
type Tank struct {
dna []int
color string
sensors [10]sensor
program []string
}
var symbols []string = []string{
"~",
"!",
"+",
"-",
"*",
"/",
"%",
"&",
"|",
"^",
"<<",
">>",
">",
">=",
"<",
"<=",
"=",
"<>",
"abs",
"dup",
"pop",
"exch",
"if",
"ifelse",
"mset",
"mget",
"{",
"}",
"fire-ready?",
"fire!",
"set-speed!",
"get-turret",
"set-turret!",
"sensor?",
"set-led!",
"random",
}
func make_nucleotide() int {
switch rand.Intn(3) {
case 0:
return rand.Intn(len(symbols))
case 1:
return len(symbols) + rand.Intn(720)
case 2:
return len(symbols) + 720 + rand.Intn(100)
}
return 0
}
func make_dna(size int) []int {
ret := make([]int, size)
for i := 0; i < size; i += 1 {
ret[i] = make_nucleotide()
}
return ret
}
func color_of_dna(dna []int) (string, []int) {
if len(dna) >= 3 {
r := dna[0] % 256
g := dna[1] % 256
b := dna[2] % 256
color := fmt.Sprintf("%02x%02x%02x", r, g, b)
return color, dna[3:]
} else {
return "cccccc", dna
}
}
func sensor_of_dna(dna []int) (sensor, []int) {
if len(dna) >= 4 {
return sensor{dna[0], dna[1], dna[2], dna[3] % 2 == 0}, dna[4:]
} else {
return sensor{0, 0, 0, false}, dna
}
}
func program_of_dna(dna []int) []string {
ret := make([]string, 0, len(dna))
stacks := []int{}
for i := 0; i < len(dna); i += 1 {
// If we are in a stack, decrement the size
if len(stacks) > 0 {
stacks[0] -= 1
// Are we at the end of the stack? Output the symbol and pop the stack off.
if stacks[0] <= 0 {
ret = append(ret, "}")
stacks = stacks[1:]
}
}
// Consider the next nucleotide
n := dna[i]
// Symbol?
if n < len(symbols) {
ret = append(ret, symbols[n])
continue
}
n -= len(symbols)
// Number?
if n < 720 {
ret = append(ret, strconv.Itoa(n - 360))
continue
}
n -= 720
// Stack.
// XXX: This doesn't seem right, we shouldn't see a ton of "} } }" at the end
ret = append(ret, "{")
stacks = append(stacks, (n + 1) % (len(dna) - i))
}
for len(stacks) > 0 {
ret = append(ret, "}")
stacks = stacks[1:]
}
return ret
}
func tank_of_dna(dna []int) Tank {
ret := Tank{dna: dna}
ret.color, dna = color_of_dna(dna)
for i := 0; i < len(ret.sensors); i += 1 {
ret.sensors[i], dna = sensor_of_dna(dna)
}
ret.program = program_of_dna(dna)
return ret
}
func twrite(num int, fn string, contents string) {
path := fmt.Sprintf("tank.%d/%s", num, fn)
ioutil.WriteFile(path, []byte(contents), 0644)
}
func (t Tank) Write(num int, dir string) {
twrite(num, "color", t.color)
for i := 0; i < len(t.sensors); i += 1 {
turret := 0
if t.sensors[i].turret {
turret = 1
}
twrite(
num,
fmt.Sprintf("sensor%d", i),
fmt.Sprintf("%d %d %d %d", t.sensors[i].srange, t.sensors[i].angle, t.sensors[i].width, turret),
)
}
twrite(num, "program", strings.Join(t.program, " "))
s, _ := json.Marshal(t.dna)
twrite(num, "dna", string(s))
}
func init() {
rand.Seed(time.Now().UTC().UnixNano())
}
func main() {
d := make_dna(200)
t := tank_of_dna(d)
t.Write(0, ".")
}