void GameState::generateMoves(MoveArray & moves, const IDType & playerIndex) const { moves.clear(); // which is the enemy player IDType enemyPlayer = getEnemy(playerIndex); // we are interested in all simultaneous moves // so return all units which can move at the same time as the first TimeType firstUnitMoveTime = getUnit(playerIndex, 0).firstTimeFree(); for (IDType unitIndex(0); unitIndex < _numUnits[playerIndex]; ++unitIndex) { // unit reference const Unit & unit(getUnit(playerIndex,unitIndex)); // if this unit can't move at the same time as the first if (unit.firstTimeFree() != firstUnitMoveTime) { // stop checking break; } if (unit.previousMoveTime() == _currentTime && _currentTime != 0) { // CHANGED // printf("ERROR: Previous Move: %s\n", unit.previousMove().moveString().c_str()); int a = 6; } moves.addUnit(); // generate attack moves if (unit.canAttackNow()) { for (IDType u(0); u<_numUnits[enemyPlayer]; ++u) { const Unit & enemyUnit(getUnit(enemyPlayer, u)); if (unit.canAttackTarget(enemyUnit, _currentTime) && enemyUnit.isAlive()) { moves.add(Move(unitIndex, playerIndex, MoveTypes::ATTACK, u)); } } } else if (unit.canHealNow()) { for (IDType u(0); u<_numUnits[playerIndex]; ++u) { // units cannot heal themselves in broodwar if (u == unitIndex) { continue; } const Unit & ourUnit(getUnit(playerIndex, u)); if (unit.canHealTarget(ourUnit, _currentTime) && ourUnit.isAlive()) { moves.add(Move(unitIndex, playerIndex, MoveTypes::HEAL, u)); } } } // generate the wait move if it can't attack yet else { if (!unit.canHeal()) { moves.add(Move(unitIndex, playerIndex, MoveTypes::RELOAD, 0)); } } // generate movement moves if (unit.isMobile()) { for (IDType d(0); d<Search::Constants::Num_Directions; ++d) { Move move(unitIndex, playerIndex, MoveTypes::MOVE, d); Position dest; Position dir(Search::Constants::Move_Dir[move._moveIndex][0], Search::Constants::Move_Dir[move._moveIndex][1]); // if the unit can attack the destination will be set by default move distance if (unit.canAttackNow() || unit.canHeal()) { dest = Position(Search::Constants::Move_Distance*dir.x(), Search::Constants::Move_Distance*dir.y()) + unit.pos(); } // otherwise it will be a move until attack move else { dest = unit.getMoveUntilAttackDest(dir, _currentTime); } if (isWalkable(dest)) { moves.add(Move(unitIndex, playerIndex, MoveTypes::MOVE, d)); } } } // if no moves were generated for this unit, it must be issued a 'PASS' move if (moves.numMoves(unitIndex) == 0) { moves.add(Move(unitIndex, playerIndex, MoveTypes::PASS, 0)); } } if (!moves.validateMoves()) { fprintf(stderr, "Moves invalid, something is wrong\n"); } }
void GameState::generateMoves(MoveArray & moves, const IDType & playerIndex) const { moves.clear(); // which is the enemy player IDType enemyPlayer = getEnemy(playerIndex); // make sure this player can move right now const IDType canMove(whoCanMove()); if (canMove == enemyPlayer) { System::FatalError("GameState Error - Called generateMoves() for a player that cannot currently move"); } // we are interested in all simultaneous moves // so return all units which can move at the same time as the first TimeType firstUnitMoveTime = getUnit(playerIndex, 0).firstTimeFree(); for (IDType unitIndex(0); unitIndex < _numUnits[playerIndex]; ++unitIndex) { // unit reference const Unit & unit(getUnit(playerIndex,unitIndex)); // if this unit can't move at the same time as the first if (unit.firstTimeFree() != firstUnitMoveTime) { // stop checking break; } if (unit.previousActionTime() == _currentTime && _currentTime != 0) { System::FatalError("Previous Move Took 0 Time: " + unit.previousAction().moveString()); } moves.addUnit(); // generate attack moves if (unit.canAttackNow()) { for (IDType u(0); u<_numUnits[enemyPlayer]; ++u) { const Unit & enemyUnit(getUnit(enemyPlayer, u)); bool invisible = false; if (enemyUnit.type().hasPermanentCloak()) { invisible = true; for (IDType detectorIndex(0); detectorIndex < _numUnits[playerIndex]; ++detectorIndex) { // unit reference const Unit & detector(getUnit(playerIndex, detectorIndex)); if (detector.type().isDetector() && detector.canSeeTarget(enemyUnit, _currentTime)) { invisible = false; break; } } } if (!invisible && unit.canAttackTarget(enemyUnit, _currentTime) && enemyUnit.isAlive()) { moves.add(Action(unitIndex, playerIndex, ActionTypes::ATTACK, u)); //moves.add(Action(unitIndex, playerIndex, ActionTypes::ATTACK, unit.ID())); } } } else if (unit.canHealNow()) { for (IDType u(0); u<_numUnits[playerIndex]; ++u) { // units cannot heal themselves in broodwar if (u == unitIndex) { continue; } const Unit & ourUnit(getUnit(playerIndex, u)); if (unit.canHealTarget(ourUnit, _currentTime) && ourUnit.isAlive()) { moves.add(Action(unitIndex, playerIndex, ActionTypes::HEAL, u)); //moves.add(Action(unitIndex, playerIndex, ActionTypes::HEAL, unit.ID())); } } } // generate the wait move if it can't attack yet else { if (!unit.canHeal()) { moves.add(Action(unitIndex, playerIndex, ActionTypes::RELOAD, 0)); } } // generate movement moves if (unit.isMobile()) { // In order to not move when we could be shooting, we want to move for the minimum of: // 1) default move distance move time // 2) time until unit can attack, or if it can attack, the next cooldown double timeUntilAttack = unit.nextAttackActionTime() - getTime(); timeUntilAttack = timeUntilAttack == 0 ? unit.attackCooldown() : timeUntilAttack; // the default move duration double defaultMoveDuration = (double)Constants::Move_Distance / unit.speed(); // if we can currently attack double chosenTime = timeUntilAttack != 0 ? std::min(timeUntilAttack, defaultMoveDuration) : defaultMoveDuration; // the chosen movement distance PositionType moveDistance = (PositionType)(chosenTime * unit.speed()); // DEBUG: If chosen move distance is ever 0, something is wrong if (moveDistance == 0) { System::FatalError("Move Action with distance 0 generated. timeUntilAttack:"+ std::to_string(timeUntilAttack)+", speed:"+std::to_string(unit.speed())); } // we are only generating moves in the cardinal direction specified in common.h for (IDType d(0); d<Constants::Num_Directions; ++d) { // the direction of this movement Position dir(Constants::Move_Dir[d][0], Constants::Move_Dir[d][1]); if (moveDistance == 0) { printf("%lf %lf %lf\n", timeUntilAttack, defaultMoveDuration, chosenTime); } // the final destination position of the unit Position dest = unit.pos() + Position(moveDistance*dir.x(), moveDistance*dir.y()); // if that poisition on the map is walkable if (isWalkable(dest) || (unit.type().isFlyer() && isFlyable(dest))) { // add the move to the MoveArray moves.add(Action(unitIndex, playerIndex, ActionTypes::MOVE, d, dest)); } } } // if no moves were generated for this unit, it must be issued a 'PASS' move if (moves.numMoves(unitIndex) == 0) { moves.add(Action(unitIndex, playerIndex, ActionTypes::PASS, 0)); } } }