#include "simulator.h" 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) { 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::decodeInstructionParameters(Task& task, const Instruction& instruction) { 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: break; case LL: task.a = instruction.a; task.b = instruction.b; break; case LV: task.a = instruction.a; break; case VL: task.b = instruction.b; break; case VV: 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; break; case LVL: task.a = instruction.a; task.c = instruction.c; break; case LVV: task.a = instruction.a; break; case VLL: task.b = instruction.b; task.c = instruction.c; break; case VLV: task.b = instruction.b; break; case VVL: task.c = instruction.c; break; case VVV: break; } //@todo: remove dummy! task.var_a = &task.a; task.var_b = &task.b; task.var_c = &task.c; } void Simulator::loadProgram(QColor color, size_t x, size_t y) { shared_ptr team = make_shared(color); programs.push_back(Program(team, Right, Position{x, y}, 2, 50, 1)); programs.back().banks[0] = make_shared(team, vector{ Instruction(BJUMP, LL, 1, 0) }); programs.back().banks[1] = make_shared(team, vector{ Instruction(SCAN, V, 1), Instruction(CREATE, LLL, 2, 50, 1), Instruction(TRANS, LL, 0, 0), Instruction(TRANS, LL, 1, 1), Instruction(TURN, L, 0), Instruction(AJUMP, L, 0) }); } 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; }); programs.erase(it, programs.end()); } void Simulator::simulate() { removeErroneousPrograms(); for (auto& program : programs) { auto& taskIndex = program.taskIndex; // 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]; decodeInstructionParameters(task, instruction); 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; break; case MOVE: if(program.mobile < 1) { program.error = MobilityRequired; break; } task.remainingCycles = 20; break; case DIE: task.remainingCycles = 1; break; case TRANS: { if(program.instructionSet < 1) { program.error = HigherInstructionSetRequired; break; } if(task.a < 0 || task.a >= program.banks.size()) { program.error = InvalidBankNumber; break; } const auto& bank = program.banks[task.a]; task.remainingCycles = 14 + (bank ? bank->instructions.size() : 0); break; } case RTRANS: { if(program.instructionSet < 1) { program.error = HigherInstructionSetRequired; break; } if(task.a < 0 || task.a >= 50 || task.b < 0 || task.b >= program.banks.size()) { program.error = InvalidBankNumber; break; } auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); task.remainingCycles = 14 + remoteProgram ? (task.a < remoteProgram->banks.size() ? (remoteProgram->banks[task.a] ? remoteProgram->banks[task.a]->instructions.size() : 0): 0): 0; break; } case TURN: task.remainingCycles = 8; break; case JUMP: task.remainingCycles = 1; break; case AJUMP: task.remainingCycles = 1; break; case BJUMP: task.remainingCycles = 2; break; case SCAN: if(program.instructionSet < 1) { program.error = HigherInstructionSetRequired; break; } task.remainingCycles = 8; break; case FARSCAN: if(program.instructionSet < 1) { program.error = HigherInstructionSetRequired; break; } task.remainingCycles = 10 + 3 * task.c; break; case SET: task.remainingCycles = 2; break; case ADD: task.remainingCycles = 2; break; case SUB: task.remainingCycles = 2; break; case MUL: task.remainingCycles = 2; break; case DIV: task.remainingCycles = 2; break; case MOD: task.remainingCycles = 2; break; case MIN: task.remainingCycles = 2; break; case MAX: task.remainingCycles = 2; break; case RANDOM: if(task.b > task.c) { program.error = InvalidParameter; break; } task.remainingCycles = 1; break; case IF: task.remainingCycles = 2; break; case IFN: task.remainingCycles = 2; break; case IFG: task.remainingCycles = 2; break; case IFL: task.remainingCycles = 2; break; case IFGE: task.remainingCycles = 2; break; case IFLE: task.remainingCycles = 2; break; case INIT: task.remainingCycles = 2; break; case BREAK: task.remainingCycles = 1; break; case RESUME: task.remainingCycles = 1; break; case SEIZE: task.remainingCycles = 1; break; case SLEEP: if(task.a > 2000) { program.error = InvalidParameter; break; } task.remainingCycles = task.a; break; case QUIT: task.remainingCycles = 1; 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)); } task.instIndex += 1; } 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.b < remoteProgram->banks.size() && task.a < program.banks.size()) { remoteProgram->banks[task.b] = program.banks[task.a]; } task.instIndex += 1; break; } case RTRANS: { auto remotePosition = calcPosition(program.position, task.direction, 1); auto remoteProgram = findProgram(remotePosition); if(remoteProgram && task.a < remoteProgram->banks.size() && task.b < program.banks.size()) { program.banks[task.b] = remoteProgram->banks[task.a]; } 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; break; } case BJUMP: { task.bankIndex = task.a; task.instIndex = task.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.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; default: task.instIndex += 1; break; } } #if 0 void Simulator::simulatex() { for (auto& program : programs) { auto& taskIndex = program.taskIndex; if(program.error != NoError) { continue; } if(program.tasks.empty()) { continue; } 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->instructions.size()) { continue; } const auto& bank = *bank_ptr; const auto& inst = bank.instructions[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.team, 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->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.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->team == program.team ? 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