393 lines
15 KiB
C
393 lines
15 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "maps.h"
|
|
#include "program.h"
|
|
#include "ant.h"
|
|
|
|
#define TICKS 2000
|
|
#define MAX_OPS 64
|
|
#define NUM_CHANNELS 4
|
|
|
|
const char map_offsets[] = { 0, -MAP_WIDTH , 1, MAP_WIDTH, -1 };
|
|
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
|
|
#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
|
|
#define cell_from(org, dir) (org) + map_offsets[(dir)]
|
|
#define reg_id(rid) (rid) < 8 ? (rid) : (rid) - 0x80
|
|
|
|
#define get_value(ant, v) (v) < 0x80 ? (v) : get_reg((ant), (v))
|
|
|
|
static inline unsigned int rnum(unsigned int* rstate) {
|
|
unsigned int e = *rstate + 0x9e3779b9;
|
|
*rstate = e;
|
|
e = (e ^ (e >> 16)) * 0x85ebca6b;
|
|
e = (e ^ (e >> 13)) * 0xc2b2ae35;
|
|
e = e ^ (e >> 16);
|
|
//e = e / 0x100000000;
|
|
return e;
|
|
}
|
|
|
|
// static inline unsigned int rnum(unsigned int* rstate) {
|
|
// return rand();
|
|
// }
|
|
|
|
|
|
|
|
static inline void set_reg(struct Ant* ant, unsigned char rid, unsigned int v) {
|
|
switch(reg_id(rid)) {
|
|
case 0:
|
|
ant->r0 = v;
|
|
break;
|
|
case 1:
|
|
ant->r1 = v;
|
|
break;
|
|
case 2:
|
|
ant->r2 = v;
|
|
break;
|
|
case 3:
|
|
ant->r3 = v;
|
|
break;
|
|
case 4:
|
|
ant->r4 = v;
|
|
break;
|
|
case 5:
|
|
ant->r5 = v;
|
|
break;
|
|
case 6:
|
|
ant->r6 = v;
|
|
break;
|
|
case 7:
|
|
ant->r7 = v;
|
|
break;
|
|
}
|
|
}
|
|
static inline unsigned int get_reg(struct Ant* ant, unsigned char rid) {
|
|
switch(reg_id(rid)) {
|
|
case 0:
|
|
return ant->r0;
|
|
case 1:
|
|
return ant->r1;
|
|
case 2:
|
|
return ant->r2;
|
|
case 3:
|
|
return ant->r3;
|
|
case 4:
|
|
return ant->r4;
|
|
case 5:
|
|
return ant->r5;
|
|
case 6:
|
|
return ant->r6;
|
|
case 7:
|
|
return ant->r7;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
#define debug(msg, ...) printf((msg), ##__VA_ARGS__)
|
|
#else
|
|
#define debug(msg, ...)
|
|
#endif
|
|
|
|
#define debug_ant(ant) debug("A%d @%d:%d R:%02x %02x %02x %02x %02x %02x %02x %02x\n", (ant)->id, (ant)->cell % MAP_WIDTH, (ant)->cell / MAP_WIDTH, (ant)->r0, (ant)->r1, (ant)->r2, (ant)->r3, (ant)->r4, (ant)->r5, (ant)->r6, (ant)->r7)
|
|
#define debug_ins(pc, ins) debug("I%02d %s %02x %02x %02x\n", pc, instruction_names[ins->op], ins->arg1, ins->arg2, ins->arg3)
|
|
|
|
int run_simulations(struct Maps* maps, struct Program* program) {
|
|
int ops;
|
|
int cost;
|
|
int score;
|
|
int dirs[4];
|
|
unsigned int rstate;
|
|
int cell;
|
|
int found_dirs = 0;
|
|
struct Map* map;
|
|
int v1, v2, v3, res;
|
|
struct Ant* ant;
|
|
unsigned int pc;
|
|
struct Instruction* ins;
|
|
struct Ant* ants = create_ants();
|
|
unsigned char food[NUM_CELLS];
|
|
unsigned char smells[NUM_CHANNELS][NUM_CELLS];
|
|
|
|
|
|
for (unsigned int mi = 0; mi < maps->num_maps; mi++) {
|
|
map = &maps->maps + mi;
|
|
debug("MAP %s\n", map->name);
|
|
memset(&smells, 0, NUM_CHANNELS * NUM_CELLS);
|
|
reset_ants(ants, map->nest);
|
|
score = 0;
|
|
rstate = 2;
|
|
for (int i = 0; i < NUM_CELLS; i++ ) {
|
|
food[i] = map->cells[i] == CELL_FOOD ? 1 : 0;
|
|
}
|
|
|
|
for (int t=0; t < TICKS; t++) {
|
|
for (int ai=0; ai < NUM_ANTS; ai++) {
|
|
|
|
ops = 0;
|
|
while (ops < MAX_OPS) {
|
|
cost = 1;
|
|
ant = &ants[ai];
|
|
pc = ant->pc;
|
|
ins = &program->instructions + pc;
|
|
debug_ins(pc, ins);
|
|
ant->pc = (pc + 1) % program->num_instructions;
|
|
switch(ins->op) {
|
|
case INS_ID:
|
|
set_reg(ant, ins->arg1, ant->id);
|
|
break;
|
|
case INS_TAG:
|
|
//noop
|
|
break;
|
|
case INS_MOVE:
|
|
v1 = get_value(ant, ins->arg1);
|
|
if (v1 == 5) {
|
|
v1 = rnum(&rstate) % 4 + 1;
|
|
}
|
|
cell = cell_from(ant->cell, v1);
|
|
if (cell > 0 && cell < NUM_CELLS && map->cells[cell] != CELL_WALL) {
|
|
ant->cell = cell;
|
|
}
|
|
cost += MAX_OPS;
|
|
break;
|
|
case INS_SENSE:
|
|
v1 = get_value(ant, ins->arg1);
|
|
v2 = ins->arg2;
|
|
found_dirs = 0;
|
|
|
|
if (map->cells[cell_from(ant->cell, 1)] == v1) dirs[found_dirs++] = 1;
|
|
if (map->cells[cell_from(ant->cell, 2)] == v1) dirs[found_dirs++] = 2;
|
|
if (map->cells[cell_from(ant->cell, 3)] == v1) dirs[found_dirs++] = 3;
|
|
if (map->cells[cell_from(ant->cell, 4)] == v1) dirs[found_dirs++] = 4;
|
|
if (found_dirs == 0) {
|
|
res = 0;
|
|
} else if (found_dirs == 1) {
|
|
res = dirs[0];
|
|
} else {
|
|
res = dirs[rnum(&rstate) % found_dirs];
|
|
}
|
|
set_reg(ant, v2, res);
|
|
break;
|
|
|
|
case INS_PROBE:
|
|
v1 = get_value(ant, ins->arg1);
|
|
v1 = cell_from(ant->cell, v1);
|
|
v1 = map->cells[v1];
|
|
set_reg(ant, ins->arg2, v1);
|
|
break;
|
|
case INS_PICKUP:
|
|
debug("PICKUP?\n");
|
|
if (!ant->carrying && food[ant->cell] > 0) {
|
|
ant->carrying = food[ant->cell]--;
|
|
debug("PICKUP!\n");
|
|
}
|
|
cost += MAX_OPS;
|
|
break;
|
|
case INS_CARRYING:
|
|
set_reg(ant, ins->arg1, ant->carrying);
|
|
break;
|
|
case INS_DROP:
|
|
debug("DROP?\n");
|
|
if (ant->carrying) {
|
|
ant -> carrying = 0;
|
|
if (map->nest == ant->cell){
|
|
debug("DROP SCORE\n");
|
|
score += 1;
|
|
} else {
|
|
food[ant->cell] += 1;
|
|
debug("DROP!\n");
|
|
}
|
|
}
|
|
cost += MAX_OPS;
|
|
break;
|
|
case INS_SMELL:
|
|
v1 = get_value(ant, ins->arg1);
|
|
v2 = ins->arg2;
|
|
res = 0;
|
|
|
|
v3 = smells[v1][cell_from(ant->cell, 1)];
|
|
if (v3 > res) found_dirs = 0;
|
|
if (v3 >= res) dirs[found_dirs++] = 1;
|
|
|
|
v3 = smells[v1][cell_from(ant->cell, 2)];
|
|
if (v3 > res) found_dirs = 0;
|
|
if (v3 >= res) dirs[found_dirs++] = 2;
|
|
|
|
v3 = smells[v1][cell_from(ant->cell, 3)];
|
|
if (v3 > res) found_dirs = 0;
|
|
if (v3 >= res) dirs[found_dirs++] = 3;
|
|
|
|
v3 = smells[v1][cell_from(ant->cell, 4)];
|
|
if (v3 > res) found_dirs = 0;
|
|
if (v3 >= res) dirs[found_dirs++] = 4;
|
|
if (res == 0) {
|
|
res = (rnum(&rstate) % 4) + 1;
|
|
}else if (found_dirs == 1) {
|
|
res = dirs[0];
|
|
} else {
|
|
res = dirs[rnum(&rstate) % found_dirs];
|
|
}
|
|
set_reg(ant, v2, res);
|
|
break;
|
|
case INS_SNIFF:
|
|
v1 = ins->arg1;
|
|
v2 = get_value(ant, ins->arg2);
|
|
v2 = cell_from(ant->cell, v2);
|
|
v1 = smells[v1][v2];
|
|
set_reg(ant, ins->arg3, v1);
|
|
break;
|
|
case INS_MARK:
|
|
v1 = get_value(ant, ins->arg1);
|
|
v2 = get_value(ant, ins->arg2);
|
|
v3 = smells[v1][ant->cell];
|
|
smells[v1][ant->cell] = MAX(v3+v2, 255);
|
|
break;
|
|
case INS_SET:
|
|
set_reg(ant, ins->arg1, ins->arg2);
|
|
break;
|
|
case INS_AND:
|
|
v1 = get_value(ant, ins->arg1);
|
|
v2 = get_value(ant, ins->arg2);
|
|
set_reg(ant, ins->arg1, v1 & v2);
|
|
break;
|
|
case INS_OR:
|
|
v1 = get_value(ant, ins->arg1);
|
|
v2 = get_value(ant, ins->arg2);
|
|
set_reg(ant, ins->arg1, v1 | v2);
|
|
break;
|
|
case INS_XOR:
|
|
v1 = get_value(ant, ins->arg1);
|
|
v2 = get_value(ant, ins->arg2);
|
|
set_reg(ant, ins->arg1, v1 | v2);
|
|
break;
|
|
case INS_LSHIFT:
|
|
v1 = get_value(ant, ins->arg1);
|
|
v2 = get_value(ant, ins->arg2);
|
|
set_reg(ant, ins->arg1, v1 << v2);
|
|
break;
|
|
case INS_RSHIFT:
|
|
v1 = get_value(ant, ins->arg1);
|
|
v2 = get_value(ant, ins->arg2);
|
|
set_reg(ant, ins->arg1, v1 >> v2);
|
|
break;
|
|
case INS_ADD:
|
|
v1 = get_value(ant, ins->arg1);
|
|
v2 = get_value(ant, ins->arg2);
|
|
set_reg(ant, ins->arg1, v1 + v2);
|
|
break;
|
|
case INS_SUB:
|
|
v1 = get_value(ant, ins->arg1);
|
|
v2 = get_value(ant, ins->arg2);
|
|
set_reg(ant, ins->arg1, v1 - v2);
|
|
break;
|
|
case INS_MUL:
|
|
v1 = get_value(ant, ins->arg1);
|
|
v2 = get_value(ant, ins->arg2);
|
|
set_reg(ant, ins->arg1, v1 * v2);
|
|
break;
|
|
case INS_DIV:
|
|
v1 = get_value(ant, ins->arg1);
|
|
v2 = get_value(ant, ins->arg2);
|
|
if (v2 != 0) {
|
|
set_reg(ant, ins->arg1, v1 / v2);
|
|
}
|
|
break;
|
|
case INS_MOD:
|
|
v1 = get_value(ant, ins->arg1);
|
|
v2 = get_value(ant, ins->arg2);
|
|
set_reg(ant, ins->arg1, v1 % v2);
|
|
break;
|
|
case INS_RANDOM:
|
|
v2 = get_value(ant, ins->arg2);
|
|
if (v2 != 0) {
|
|
v1 = rnum(&rstate) % v2;
|
|
set_reg(ant, ins->arg1, v1);
|
|
}
|
|
break;
|
|
case INS_SJR:
|
|
ant->jr = *((int*)ins) >> 8;
|
|
break;
|
|
case INS_MJR:
|
|
ant->jr = get_value(ant, ins->arg1);
|
|
break;
|
|
case INS_JMP:
|
|
ant->pc = ant->jr;
|
|
break;
|
|
case INS_CALL:
|
|
set_reg(ant, ins->arg1, ant->pc);
|
|
ant->pc = ant->jr;
|
|
break;
|
|
case INS_JEQ:
|
|
v1 = get_value(ant, ins->arg1);
|
|
v2 = get_value(ant, ins->arg2);
|
|
if (v1 == v2) {
|
|
ant->pc = ant->jr;
|
|
}
|
|
break;
|
|
case INS_JNE:
|
|
v1 = get_value(ant, ins->arg1);
|
|
v2 = get_value(ant, ins->arg2);
|
|
if (v1 != v2) {
|
|
ant->pc = ant->jr;
|
|
}
|
|
break;
|
|
case INS_JGT:
|
|
v1 = get_value(ant, ins->arg1);
|
|
v2 = get_value(ant, ins->arg2);
|
|
if (v1 > v2) {
|
|
ant->pc = ant->jr;
|
|
}
|
|
break;
|
|
case INS_JLT:
|
|
v1 = get_value(ant, ins->arg1);
|
|
v2 = get_value(ant, ins->arg2);
|
|
if (v1 < v2) {
|
|
ant->pc = ant->jr;
|
|
}
|
|
break;
|
|
};
|
|
debug_ant(ant);
|
|
ops += cost;
|
|
}
|
|
}
|
|
for (int pi = 0; pi < NUM_CHANNELS; pi++) {
|
|
for (int ci = 0; ci < NUM_CELLS; ci++) {
|
|
if (smells[pi][ci] > 0) smells[pi][ci]--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return score;
|
|
}
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
struct Program* program = NULL;
|
|
struct Maps* maps = NULL;
|
|
int score = 0;
|
|
if (argc < 3) {
|
|
printf("Usage: farm <maps file> <program file>\n");
|
|
return 1;
|
|
}
|
|
char* maps_filename = argv[1];
|
|
char* program_filename = argv[2];
|
|
|
|
maps = load_maps(maps_filename);
|
|
program = load_program(program_filename);
|
|
int result = 0;
|
|
if (maps == NULL) {
|
|
printf("Could not load maps\n");
|
|
result = 2;
|
|
}else if (program == NULL) {
|
|
printf("Could not load program\n");
|
|
result = 3;
|
|
} else {
|
|
score = run_simulations(maps, program);
|
|
}
|
|
free(program);
|
|
free(maps);
|
|
printf("Score: %d\n", score);
|
|
return result;
|
|
}
|