#ifndef SIMULATOR_H #define SIMULATOR_H #include #include #include #include #include using namespace std; enum { CYCLE_TIMEOUT = 100000, FIELDS_XY = 20, }; struct Position { size_t x = 0; size_t y = 0; Position() : x(0) , y(0) {} Position(size_t _x, size_t _y) : x(_x) , y(_y) {} Position(const Position& _pos) : x(_pos.x) , y(_pos.y) {} }; enum Direction { Right, Down, Left, Up, }; enum Command : uint16_t { NOP, CREATE, MOVE, DIE, TRANS, RTRANS, TURN, JUMP, AJUMP, BJUMP, SCAN, FARSCAN, SET, ADD, SUB, MUL, DIV, MOD, MIN, MAX, RANDOM, IF, IFN, IFG, IFL, IFGE, IFLE, // INIT, // BREAK, // RESUME, // SEIZE, // SLEEP, // QUIT, }; enum Params : uint16_t { LLL, LLV, LVL, LVV, VLL, VLV, VVL, VVV, N = LLL, L = LLL, V = VLL, LL = LLL, LV = LVL, VL = VLL, VV = VVV, }; enum Variables { Local_0, Local_1, Local_2, Local_3, Local_4, Local_5, Local_6, Local_7, Local_8, Local_9, Local_10, Local_11, Local_12, Local_13, Local_14, Local_15, Local_16, Local_17, Local_18, Local_19, LocalActive, LocalBanks, LocalInstrSet, LocalMobile, LocalAge, LocalTasks, LocalGeneration, LocalId, RemoteActive, RemoteBanks, RemoteInstrSet, RemoteMobile, RemoteAge, RemoteTasks, RemoteGeneration, GlobalPub, GlobalTeam, GlobalOwn, GlobalOthers, GlobalFields, GlobalTime, GlobalTimeout, }; enum Error { NoError, // No error EliminationTrigger, // Elimination Trigger released DataHunger, // Data Hunger (Bank 1 empty and executed) DivisionByZero, // Division by zero InvalidBankNumber, // Invalid bank number (e.g. in TRANS or BJUMP) HigherInstructionSetRequired, // Higher Instruction Set required MobilityRequired, // Mobility required DieExecuted, // DIE executed InvalidParameter , // Invalid parameter (e.g. CREATE x, -1, x) Unemployment, // No more tasks left in a robot (Unemployment) InstructionDurationTooHigh, // Instruction duration too high (i.e. > MaxInstrDur) }; using Parameter = int32_t; struct Instruction { Command command = NOP; Params params = N; Parameter a = 0; Parameter b = 0; Parameter c = 0; Instruction(Command _command, Params _params = N, Parameter _a = 0, Parameter _b = 0, Parameter _c = 0) : command(_command) , params(_params) , a(_a) , b(_b) , c(_c) { } }; using Bank = shared_ptr>; using BankIndex = size_t; using InstIndex = size_t; using TaskIndex = size_t; struct Task { Direction direction = Right; BankIndex bankIndex = 0; InstIndex instIndex = 0; int32_t a = 0; int32_t b = 0; int32_t c = 0; int32_t* p_a = nullptr; int32_t* p_b = nullptr; int32_t* p_c = nullptr; }; struct Program { int active = 0; int instructionSet = 0; int mobile = 0; int creationCycle = 0; int generation = 0; Error error = NoError; QColor color; Position position; vector tasks; TaskIndex taskIndex = 0; vector banks; array vars{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; Program(QColor _color, Position _position, int32_t instructionSet, int32_t slotCount, int32_t mobile) { color = _color; position = _position; banks.resize(slotCount); tasks.resize(1); } }; struct Field { Program* program = nullptr; }; struct Simulator { size_t cycle = 0; vector programs; Simulator() { programs.reserve(FIELDS_XY * FIELDS_XY); // std::srand(std::time(0)); std::srand(0); } Position calcPosition(Position position, Direction direction, int32_t distance) { switch(direction) { case Right: return Position{(position.x + distance) % FIELDS_XY, position.y}; case Down: return Position{position.x, (position.y + distance) % FIELDS_XY}; case Left: return Position{(position.x - distance) % FIELDS_XY, position.y}; case Up: return Position{position.x, (position.y - distance) % FIELDS_XY}; } } void loadProgram(QColor color, size_t x, size_t y) { programs.push_back(Program(color, Position{x, y}, 2, 50, 1)); { Bank bank = make_shared>(); bank->push_back(Instruction(BJUMP, LL, 1, 0)); programs.back().banks[0] = bank; } { Bank bank = make_shared>(); bank->push_back(Instruction(SCAN, V, 1)); bank->push_back(Instruction(CREATE, LLL, 2, 50, 1)); bank->push_back(Instruction(TRANS, LL, 0, 0)); bank->push_back(Instruction(TRANS, LL, 1, 1)); // bank->push_back(Instruction(RANDOM, VLL, 5, -5, 5)); bank->push_back(Instruction(TURN, L, 0)); bank->push_back(Instruction(ADD, VL, 6, 1)); // bank->push_back(Instruction(IFG, VL, 6, 5)); // bank->push_back(Instruction(DIE)); bank->push_back(Instruction(AJUMP, L, 0)); programs.back().banks[1] = bank; } } Program* findProgram(Position position) { for(auto& program : programs) { if(program.position.x == position.x && program.position.y == position.y && program.error == NoError) { return &program; } } return nullptr; } void simulate() { for (auto& program : programs) { auto& taskIndex = program.taskIndex; if(program.error != NoError) { continue; } if(program.tasks.empty()) { continue; } taskIndex = (taskIndex + 1) % program.tasks.size(); if (taskIndex < program.tasks.size()) { auto& task = program.tasks[taskIndex]; if(task.bankIndex >= program.banks.size()) { continue; } const auto bank_ptr = program.banks[task.bankIndex].get(); if(bank_ptr == nullptr || task.instIndex >= bank_ptr->size()) { continue; } const auto& bank = *bank_ptr; const auto& inst = bank[task.instIndex]; //prevent overrideing of instuctions... auto mapParameters = [this](Program& program, Task& task, const Instruction& inst) { task.p_a = &task.a; task.p_b = &task.b; task.p_c = &task.c; auto decode = [&](int32_t*& v) { switch(*v) { case Local_0: v = &program.vars[0]; break; case Local_1: v = &program.vars[1]; break; case Local_2: v = &program.vars[2]; break; case Local_3: v = &program.vars[3]; break; case Local_4: v = &program.vars[4]; break; case Local_5: v = &program.vars[5]; break; case Local_6: v = &program.vars[6]; break; case Local_7: v = &program.vars[7]; break; case Local_8: v = &program.vars[8]; break; case Local_9: v = &program.vars[9]; break; case Local_10: v = &program.vars[10]; break; case Local_11: v = &program.vars[11]; break; case Local_12: v = &program.vars[12]; break; case Local_13: v = &program.vars[13]; break; case Local_14: v = &program.vars[14]; break; case Local_15: v = &program.vars[15]; break; case Local_16: v = &program.vars[16]; break; case Local_17: v = &program.vars[17]; break; case Local_18: v = &program.vars[18]; break; case Local_19: v = &program.vars[19]; break; case LocalActive: v = &program.active; break; case LocalBanks: *v = program.banks.size(); break; case LocalInstrSet: *v = program.instructionSet; break; case LocalMobile: *v = program.mobile; break; case LocalAge: *v = cycle - program.creationCycle; break; case LocalTasks: *v = program.tasks.size(); break; case LocalGeneration: *v = program.generation; break; case LocalId: // @todo break; case RemoteActive: { auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); if(remoteProgram) { v = &remoteProgram->active; } else { *v = 0; } break; } case RemoteBanks: { auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); *v = remoteProgram ? remoteProgram->banks.size() : 0; break; } case RemoteInstrSet: { auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); *v = remoteProgram ? remoteProgram->instructionSet : 0; break; } case RemoteMobile: { auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); *v = remoteProgram ? remoteProgram->mobile: 0; break; } case RemoteAge: { auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); *v = remoteProgram ? (cycle - remoteProgram->creationCycle) : 0; break; } case RemoteTasks: { auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); *v = remoteProgram ? remoteProgram->tasks.size() : 0; break; } case RemoteGeneration: { auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); *v = remoteProgram ? remoteProgram->generation : 0; break; } case GlobalPub: // @todo break; case GlobalTeam: // @todo break; case GlobalOwn: // @todo break; case GlobalOthers: // @todo break; case GlobalFields: *v = FIELDS_XY * FIELDS_XY; break; case GlobalTime: *v = cycle; break; case GlobalTimeout: *v = CYCLE_TIMEOUT; break; } }; switch(inst.params) { case LLL: { task.a = inst.a; task.b = inst.b; task.c = inst.c; break; } case LLV: { task.a = inst.a; task.b = inst.b; decode(task.p_c); break; } case LVL: { task.a = inst.a; decode(task.p_b); task.c = inst.c; break; } case LVV: { task.a = inst.a; decode(task.p_b); decode(task.p_c); break; } case VLL: { decode(task.p_a); task.b = inst.b; task.c = inst.c; break; } case VLV: { decode(task.p_a); task.b = inst.b; decode(task.p_c); break; } case VVL: { decode(task.p_a); decode(task.p_b); task.c = inst.c; break; } case VVV: { decode(task.p_a); decode(task.p_b); decode(task.p_c); break; } } }; mapParameters(program, task, inst); switch(inst.command) { case CREATE: { auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); if(!remoteProgram) { programs.push_back(Program(program.color, remotePosition, *task.p_a, *task.p_b, *task.p_c)); } task.instIndex += 1; break; } case MOVE: { auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); if(!remoteProgram) { program.position = remotePosition; } task.instIndex += 1; break; } case DIE: { program.error = DieExecuted; task.instIndex += 1; break; } case TRANS: { auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); if(remoteProgram && *task.p_b < remoteProgram->banks.size() && *task.p_a < program.banks.size()) { remoteProgram->banks[*task.p_b] = program.banks[*task.p_a]; } task.instIndex += 1; break; } case RTRANS: { auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); if(remoteProgram && *task.p_a < remoteProgram->banks.size() && *task.p_b < program.banks.size()) { program.banks[*task.p_b] = remoteProgram->banks[*task.p_a]; } task.instIndex += 1; break; } case TURN: { task.direction = static_cast(qMax(0, (task.direction + ((*task.p_a >= 0) ? 1 : -1)) % 4)); task.instIndex += 1; break; } case JUMP: { task.instIndex += *task.p_a; break; } case AJUMP: { task.instIndex = *task.p_a; break; } case BJUMP: { task.bankIndex = *task.p_a; task.instIndex = *task.p_b; break; } case SCAN: { // Scans a field, result in #a // #a=0 ...empty. // #a=1 ...enemy // #a=2 ...friend auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); *task.p_a = !remoteProgram ? 0 : remoteProgram->color == program.color ? 2: 1; task.instIndex += 1; break; } case FARSCAN: { // Scans up to c fields straight in front of the bot. // The nearest bot's type is stored in #a: // #a=0 ...empty. // #a=1 ...enemy // #a=2 ...friend // Its distance is stored in #b. *task.p_a = 0; *task.p_b = 0; for(int i = 0; i < *task.p_c; ++i) { auto remotePosition = calcPosition(program.position, task.direction, i + 1); auto remoteProgram = findProgram(remotePosition); if(remoteProgram) { *task.p_a = remoteProgram->color == program.color ? 2: 1; *task.p_b = i; break; } } task.instIndex += 1; break; } case SET: *task.p_a = *task.p_b; task.instIndex += 1; break; case ADD: *task.p_a += *task.p_b; task.instIndex += 1; break; case SUB: *task.p_a -= *task.p_b; task.instIndex += 1; break; case MUL: *task.p_a *= *task.p_b; task.instIndex += 1; break; case DIV: *task.p_a /= *task.p_b; task.instIndex += 1; break; case MOD: *task.p_a %= *task.p_b; task.instIndex += 1; break; case MIN: *task.p_a = qMin(*task.p_a, *task.p_b); task.instIndex += 1; break; case MAX: *task.p_a = qMax(*task.p_a, *task.p_b); task.instIndex += 1; break; case RANDOM: *task.p_a = *task.p_b + (rand() % (*task.p_c - *task.p_b + 1)); task.instIndex += 1; break; case IF: task.instIndex += ((*task.p_a == *task.p_b) ? 1 : 2); break; case IFN: task.instIndex += ((*task.p_a != *task.p_b) ? 1 : 2); break; case IFG: task.instIndex += ((*task.p_a > *task.p_b) ? 1 : 2); break; case IFL: task.instIndex += ((*task.p_a < *task.p_b) ? 1 : 2); break; case IFGE: task.instIndex += ((*task.p_a >= *task.p_b) ? 1 : 2); break; case IFLE: task.instIndex += ((*task.p_a <= *task.p_b) ? 1 : 2); break; default: task.instIndex += 1; break; } } } ++cycle; } }; #endif // SIMULATOR_H