Files
ants/farm/farm.c
2026-03-26 18:43:01 +01:00

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;
}