#include "simulator.h" enum { RemoteAccessCost = 6}; Simulator::Simulator() { programs.reserve(FIELDS_XY * FIELDS_XY); // std::srand(std::time(0)); std::srand(0); } Position Simulator::calcPosition(Position position, Direction direction, int32_t distance) { switch(direction) { default: 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 Simulator::decode(Program& program, Task& task, int& v, int*& var_v, int param, int& additionalCycles) { var_v = nullptr; switch(param) { case Local_1 : v = program.vars[0]; var_v = &program.vars[0]; break; case Local_2 : v = program.vars[1]; var_v = &program.vars[1]; break; case Local_3 : v = program.vars[2]; var_v = &program.vars[2]; break; case Local_4 : v = program.vars[3]; var_v = &program.vars[3]; break; case Local_5 : v = program.vars[4]; var_v = &program.vars[4]; break; case Local_6 : v = program.vars[5]; var_v = &program.vars[5]; break; case Local_7 : v = program.vars[6]; var_v = &program.vars[6]; break; case Local_8 : v = program.vars[7]; var_v = &program.vars[7]; break; case Local_9 : v = program.vars[8]; var_v = &program.vars[8]; break; case Local_10: v = program.vars[9]; var_v = &program.vars[9]; break; case Local_11: v = program.vars[10]; var_v = &program.vars[10]; break; case Local_12: v = program.vars[11]; var_v = &program.vars[11]; break; case Local_13: v = program.vars[12]; var_v = &program.vars[12]; break; case Local_14: v = program.vars[13]; var_v = &program.vars[13]; break; case Local_15: v = program.vars[14]; var_v = &program.vars[14]; break; case Local_16: v = program.vars[15]; var_v = &program.vars[15]; break; case Local_17: v = program.vars[16]; var_v = &program.vars[16]; break; case Local_18: v = program.vars[17]; var_v = &program.vars[17]; break; case Local_19: v = program.vars[18]; var_v = &program.vars[18]; break; case Local_20: v = program.vars[19]; var_v = &program.vars[19]; break; case LocalActive: v = program.active; var_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: v = program.team->id; break; case RemoteActive: { additionalCycles += RemoteAccessCost; auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); if(remoteProgram) { v = remoteProgram->active; var_v = &remoteProgram->active; } else { v = 0; var_v = nullptr; } break; } case RemoteBanks: { additionalCycles += RemoteAccessCost; auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); v = remoteProgram ? remoteProgram->banks.size() : 0; break; } case RemoteInstrSet: { additionalCycles += RemoteAccessCost; auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); v = remoteProgram ? remoteProgram->instructionSet : 0; break; } case RemoteMobile: { additionalCycles += RemoteAccessCost; auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); v = remoteProgram ? remoteProgram->mobile: 0; break; } case RemoteAge: { additionalCycles += RemoteAccessCost; auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); v = remoteProgram ? (cycle - remoteProgram->creationCycle) : 0; break; } case RemoteTasks: { additionalCycles += RemoteAccessCost; auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); v = remoteProgram ? remoteProgram->tasks.size() : 0; break; } case RemoteGeneration: { additionalCycles += RemoteAccessCost; auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); v = remoteProgram ? remoteProgram->generation : 0; break; } case GlobalPub: v = pub; var_v = &pub; break; case GlobalTeam: v = program.team->var; var_v = &program.team->var; break; case GlobalOwn: v = program.team->programCount; break; case GlobalOthers: v = 0; for(auto& team : teams) { if(team != program.team) { v += team->programCount; } } break; case GlobalFields: v = FIELDS_XY * FIELDS_XY; break; case GlobalTime: v = cycle; break; case GlobalTimeout: v = CYCLE_TIMEOUT; break; } } void Simulator::decodeInstructionParameters(Program& program, Task& task, const Instruction& instruction, int& additionalCycles) { task.a = instruction.a; task.b = instruction.b; task.c = instruction.c; task.var_a = nullptr; task.var_b = nullptr; task.var_c = nullptr; switch(instruction.params) { case N: break; case L: task.a = instruction.a; break; case V: decode(program, task, task.a, task.var_a, instruction.a, additionalCycles); break; case LL: task.a = instruction.a; task.b = instruction.b; break; case LV: task.a = instruction.a; decode(program, task, task.b, task.var_b, instruction.b, additionalCycles); break; case VL: decode(program, task, task.a, task.var_a, instruction.a, additionalCycles); task.b = instruction.b; break; case VV: decode(program, task, task.a, task.var_a, instruction.a, additionalCycles); decode(program, task, task.b, task.var_b, instruction.b, additionalCycles); break; case LLL: task.a = instruction.a; task.b = instruction.b; task.c = instruction.c; break; case LLV: task.a = instruction.a; task.b = instruction.b; decode(program, task, task.c, task.var_c, instruction.c, additionalCycles); break; case LVL: task.a = instruction.a; decode(program, task, task.b, task.var_b, instruction.b, additionalCycles); task.c = instruction.c; break; case LVV: task.a = instruction.a; decode(program, task, task.b, task.var_b, instruction.b, additionalCycles); decode(program, task, task.c, task.var_c, instruction.c, additionalCycles); break; case VLL: decode(program, task, task.a, task.var_a, instruction.a, additionalCycles); task.b = instruction.b; task.c = instruction.c; break; case VLV: decode(program, task, task.a, task.var_a, instruction.a, additionalCycles); task.b = instruction.b; decode(program, task, task.c, task.var_c, instruction.c, additionalCycles); break; case VVL: decode(program, task, task.a, task.var_a, instruction.a, additionalCycles); decode(program, task, task.b, task.var_b, instruction.b, additionalCycles); task.c = instruction.c; break; case VVV: decode(program, task, task.a, task.var_a, instruction.a, additionalCycles); decode(program, task, task.b, task.var_b, instruction.b, additionalCycles); decode(program, task, task.c, task.var_c, instruction.c, additionalCycles); break; } } Program* Simulator::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 Simulator::removeErroneousPrograms() { auto it = std::remove_if(programs.begin(), programs.end(), [](const Program& program){ return program.error != NoError; }); for(auto itr = it; itr != programs.end(); ++itr) { --(itr->team->programCount); } programs.erase(it, programs.end()); } void Simulator::simulate() { removeErroneousPrograms(); for (auto& program : programs) { auto& taskIndex = program.taskIndex; // do nothing if not active if(!program.active) { continue; } // error if no active task exists if(find_if(program.tasks.begin(), program.tasks.end(), [](const Task& task){ return !task.paused; }) == program.tasks.end()) { program.error = Unemployment; continue; } // switch to next unpasued task do { taskIndex = (taskIndex + 1) % program.tasks.size(); } while(program.tasks[taskIndex].paused); // check if we are still executing an instruction auto& task = program.tasks[taskIndex]; if(task.remainingCycles > 0) { --task.remainingCycles; continue; } else { endInstruction(program, task); beginInstruction(program, task); } } ++cycle; } void Simulator::beginInstruction(Program& program, Task& task) { // handle invalid bank number if(program.banks.empty() || task.bankIndex >= program.banks.size()) { program.error = InvalidBankNumber; return; } // handle autoreset to bank 0,0 if instructionIndex is out of banks bound or bank is empty if(!program.banks[task.bankIndex] || task.instIndex >= program.banks[task.bankIndex]->instructions.size()) { task.bankIndex = 0; task.instIndex = 0; } // handle data hunger if(task.bankIndex == 0 && !program.banks[0]) { program.error = DataHunger; return; } task.execBank = program.banks[task.bankIndex]; auto& instruction = program.banks[task.bankIndex]->instructions[task.instIndex]; // decode instruction parameters int additionalCycles = 0; decodeInstructionParameters(program, task, instruction, additionalCycles); // calculate cycle durations and check preconditions switch(instruction.command) { case CREATE: if(program.instructionSet < 2) { program.error = HigherInstructionSetRequired; break; } if(task.a < 0 || task.a > 2 || task.b < 1 || task.b > 50 || task.c < 0 || task.c > 1) { program.error = InvalidParameter; break; } task.remainingCycles = 100 + 50 * task.a + 25 * task.b + 120 * task.c + additionalCycles; break; case MOVE: if(program.mobile < 1) { program.error = MobilityRequired; break; } task.remainingCycles = 20 + additionalCycles; break; case DIE: task.remainingCycles = 1 + additionalCycles; break; case TRANS: { int ar = task.a - 1; //int br = task.b - 1; if(program.instructionSet < 1) { program.error = HigherInstructionSetRequired; break; } if(ar < 0 || ar >= static_cast(program.banks.size())) { program.error = InvalidBankNumber; break; } const auto& bank = program.banks[ar]; task.remainingCycles = 14 + (bank ? bank->instructions.size() : 0); break; } case RTRANS: { int ar = task.a - 1; int br = task.b - 1; if(program.instructionSet < 1) { program.error = HigherInstructionSetRequired; break; } if(ar < 0 || ar >= 50 || br < 0 || br >= static_cast(program.banks.size())) { program.error = InvalidBankNumber; break; } auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); task.remainingCycles = 14 + additionalCycles + remoteProgram ? (ar < static_cast(remoteProgram->banks.size()) ? (remoteProgram->banks[ar] ? remoteProgram->banks[ar]->instructions.size() : 0): 0): 0; break; } case TURN: task.remainingCycles = 8 + additionalCycles; break; case JUMP: task.remainingCycles = 1 + additionalCycles; break; case AJUMP: task.remainingCycles = 1 + additionalCycles; break; case BJUMP: task.remainingCycles = 2 + additionalCycles; break; case SCAN: if(program.instructionSet < 1) { program.error = HigherInstructionSetRequired; break; } task.remainingCycles = 8 + additionalCycles; break; case FARSCAN: if(program.instructionSet < 1) { program.error = HigherInstructionSetRequired; break; } task.remainingCycles = 10 + 3 * task.c; break; case SET: task.remainingCycles = 2 + additionalCycles; break; case ADD: task.remainingCycles = 2 + additionalCycles; break; case SUB: task.remainingCycles = 2 + additionalCycles; break; case MUL: task.remainingCycles = 2 + additionalCycles; break; case DIV: task.remainingCycles = 2 + additionalCycles; break; case MOD: task.remainingCycles = 2 + additionalCycles; break; case MIN: task.remainingCycles = 2 + additionalCycles; break; case MAX: task.remainingCycles = 2 + additionalCycles; break; case RANDOM: if(task.b > task.c) { program.error = InvalidParameter; break; } task.remainingCycles = 1 + additionalCycles; break; case IF: task.remainingCycles = 2 + additionalCycles; break; case IFN: task.remainingCycles = 2 + additionalCycles; break; case IFG: task.remainingCycles = 2 + additionalCycles; break; case IFL: task.remainingCycles = 2 + additionalCycles; break; case IFGE: task.remainingCycles = 2 + additionalCycles; break; case IFLE: task.remainingCycles = 2 + additionalCycles; break; case INIT: task.remainingCycles = 2 + additionalCycles; break; case BREAK: task.remainingCycles = 1 + additionalCycles; break; case RESUME: task.remainingCycles = 1 + additionalCycles; break; case SEIZE: task.remainingCycles = 1 + additionalCycles; break; case SLEEP: if(task.a > 2000) { program.error = InvalidParameter; break; } task.remainingCycles = task.a + additionalCycles; break; case QUIT: task.remainingCycles = 1 + additionalCycles; break; } } void Simulator::endInstruction(Program& program, Task& task) { // we are not executing yet if(!task.execBank || program.error != NoError) { return; } // check if bank is overwritten, skip execution of current command and begin new bank at instruction 0 if(task.execBank != program.banks[task.bankIndex]) { task.instIndex = 0; return; } // execute command auto& instruction = task.execBank->instructions[task.instIndex]; switch(instruction.command) { case CREATE: { auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); if(!remoteProgram) { programs.push_back(Program(program.team, task.direction, remotePosition, task.a, task.b, task.c)); ++program.team->programCount; } 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: { int ar = task.a - 1; int br = task.b - 1; auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); if(remoteProgram && br < static_cast(remoteProgram->banks.size()) && ar < static_cast(program.banks.size())) { remoteProgram->banks[br] = program.banks[ar]; } task.instIndex += 1; break; } case RTRANS: { int ar = task.a - 1; int br = task.b - 1; auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); if(remoteProgram && ar < static_cast(remoteProgram->banks.size()) && br < static_cast(program.banks.size())) { program.banks[br] = remoteProgram->banks[ar]; } task.instIndex += 1; break; } case TURN: { task.direction = static_cast(qMax(0, (task.direction + ((task.a >= 0) ? 1 : -1)) % 4)); task.instIndex += 1; break; } case JUMP: { task.instIndex += task.a; break; } case AJUMP: { task.instIndex = task.a - 1; break; } case BJUMP: { task.bankIndex = task.a - 1; task.instIndex = task.b - 1; 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.var_a = !remoteProgram ? 0 : remoteProgram->team == program.team ? 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.var_a = 0; *task.var_b = 0; for(int i = 0; i < task.c; ++i) { auto remotePosition = calcPosition(program.position, task.direction, i + 1); auto remoteProgram = findProgram(remotePosition); if(remoteProgram) { *task.var_a = remoteProgram->team == program.team ? 2: 1; *task.var_b = i; break; } } task.instIndex += 1; break; } case SET: *task.var_a = task.b; task.instIndex += 1; break; case ADD: *task.var_a += task.b; task.instIndex += 1; break; case SUB: *task.var_a -= task.b; task.instIndex += 1; break; case MUL: *task.var_a *= task.b; task.instIndex += 1; break; case DIV: if(task.b == 0) { program.error = DivisionByZero; break; } *task.var_a /= task.b; task.instIndex += 1; break; case MOD: if(task.b == 0) { program.error = DivisionByZero; break; } *task.var_a %= task.b; task.instIndex += 1; break; case MIN: *task.var_a = qMin(task.a, task.b); task.instIndex += 1; break; case MAX: *task.var_a = qMax(task.a, task.b); task.instIndex += 1; break; case RANDOM: *task.var_a = task.b + (rand() % (task.c - task.b + 1)); task.instIndex += 1; break; case IF: task.instIndex += ((task.a == task.b) ? 1 : 2); break; case IFN: task.instIndex += ((task.a != task.b) ? 1 : 2); break; case IFG: task.instIndex += ((task.a > task.b) ? 1 : 2); break; case IFL: task.instIndex += ((task.a < task.b) ? 1 : 2); break; case IFGE: task.instIndex += ((task.a >= task.b) ? 1 : 2); break; case IFLE: task.instIndex += ((task.a <= task.b) ? 1 : 2); break; case INIT: task.instIndex += 1; break; case BREAK: task.instIndex += 1; break; case RESUME: task.instIndex += 1; break; case SEIZE: task.instIndex += 1; break; case SLEEP: task.instIndex += 1; break; case QUIT: task.instIndex += 1; break; default: task.instIndex += 1; break; } }