/// <summary> /// Moves a unit on the game board and redraws to show updated /// positions. /// </summary> /// <param name="character">ID of the piece, so we know how to redraw.</param> /// <param name="newX">new x-coordinate position of the piece</param> /// <param name="newY">new y-coordinate position of the piece</param> /// <returns>true if the update happened, false otherwise</returns> public static Boolean update(Character character, Int16 newX, Int16 newY) { if (isValidMove(newX, newY)) { Logger.log(@"Valid move detected, attempting to reassign position.", "debug"); overlay[character.yPos, character.xPos] = board[character.yPos, character.xPos]; overlay[newY, newX] = character.id; character.xPos = newX; character.yPos = newY; Logger.log(@"Reassignment successful, map should redraw.", "debug"); redraw(); return true; } else { Logger.log(String.Format(@"Invalid move detected. Tried to place at {0}, {1}", newX, newY), "warning"); return false; } }
private int isMovingToInterceptOpponent(Character unit, List<Character> enemy_units, int prev_x, int prev_y) { // as long as the cpu unit is moving toward another player unit, return true foreach(Character e_unit in enemy_units) { if(GameBoard.distance(unit.xPos, unit.yPos, e_unit.xPos, e_unit.yPos) < GameBoard.distance(prev_x, prev_y, e_unit.xPos, e_unit.yPos)){ return HEURISTIC_INTERCEPT_OPPONENT; } } return 0; }
private int isTravellingToBase(Character unit, int prev_x, int prev_y, Tuple<int,int> basePos, Boolean isEnemyBase = true) { // if the base is not defined, the unit can't be traveling to it if(basePos.Item1 < 0) return 0; if (GameBoard.distance(unit.xPos, unit.yPos, basePos.Item1, basePos.Item2) < GameBoard.distance(prev_x, prev_y, basePos.Item1, basePos.Item2)) { if(isEnemyBase){ return HEURISTIC_TRAVEL_TO_ENEMY_BASE; } else { int numGuarding = 0; foreach (EnemyCharacter friendly in cpuUnits) { if (GameBoard.distance(friendly.xPos, friendly.yPos, cpuBasePos.Item1, cpuBasePos.Item2) < 3) { numGuarding++; } } return HEURISTIC_TRAVEL_TO_HOME_BASE - numGuarding; } } else {return 0; } }
private int isGuardingBase(Character unit, Tuple<int,int> basePos) { if(GameBoard.distance(unit.xPos, unit.yPos, basePos.Item1, basePos.Item2) < 2) { return HEURISTIC_GUARD_BASE; } else { return 0; } }
private int isFleeingFromOverwhelmingForce(Character unit, List<Character> enemy_units, List<Character> friendly_units, int prev_x, int prev_y) { List<Character> unitsToConsider = new List<Character>(); int enemy_count = 0; int unit_count = 0; // get all units within 3 spaces of the current unit foreach( Character enemy in enemy_units){ if(GameBoard.distance(enemy.xPos, enemy.yPos, unit.xPos, unit.yPos) <= 3) { unitsToConsider.Add(enemy); enemy_count++; } } foreach( Character friendly_unit in friendly_units){ // don't include the given unit if(friendly_unit == unit) continue; if(GameBoard.distance(friendly_unit.xPos, friendly_unit.yPos, unit.xPos, unit.yPos) <= 3){ unit_count++; } } // if the player force is at least the overwhelming percentage, fleeing is an option if(enemy_count/(unit_count+1) > OVERWHELMING_FORCE_PERCENT) { // now we need to get which direction they are all mainly coming from // get the average x and y vector they are coming from float avg_x = 0.0f; float avg_y = 0.0f; List<int> vectors = new List<int>(); foreach( Character enemy in unitsToConsider) { avg_x += (enemy.xPos-unit.xPos); avg_y += (enemy.yPos-unit.yPos); } // if the resultant additions so far are very small, we're surrounded, so there's nowhere to run to if((avg_x < 1 && avg_x > -1) && (avg_y < 1 && avg_y > -1)) { return 0;} // get the unit vector of current motion float motion_x = unit.xPos - prev_x; float motion_y = unit.yPos - prev_y; motion_x = motion_x/((float) Math.Sqrt(motion_x*motion_x + motion_y*motion_y)); motion_y = motion_x/((float) Math.Sqrt(motion_x*motion_x + motion_y*motion_y)); // make the averages unit vectors avg_x = avg_x/((float) Math.Sqrt(avg_x*avg_x + avg_y*avg_y)); avg_y = avg_y/((float) Math.Sqrt(avg_x*avg_x + avg_y*avg_y)); // now just get the angle between the vectors float theta = (float) Math.Acos(motion_x*avg_x + motion_y*avg_y); // if current motion is greater than 90 degrees away from the enemy positions, unit is fleeing if(theta > Math.PI/2){ return HEURISTIC_FLEE_FROM_OVER_FORCE; } else { return 0; } } else { return 0; } }
private int canDamageOpponentBase(Character unit, Tuple<int,int> basePos) { if(basePos.Item1 < 0) return 0; if (GameBoard.distance(unit.xPos, unit.yPos, basePos.Item1, basePos.Item2) < 2) { return HEURISTIC_DAMAGE_OPPONENT_BASE; } else { return 0; } }
private int canAttackOpponent(Character unit, List<Character> enemyUnits) { List<Character> attackable = GameBoard.getAttackableUnits(unit.xPos, unit.yPos, enemyUnits); if (attackable.Any()) { return HEURISTIC_ATTACK_OPPONENT_UNIT; } return 0; }
private int canAttackBaseAttacker(Character unit, List<Character> enemy_units, Tuple<int,int> base_pos) { // if there is no base, obviously this will never be true if(base_pos.Item1 < 0) return 0; foreach(Character enemy_unit in enemy_units){ if(GameBoard.distance(enemy_unit.xPos, enemy_unit.yPos, base_pos.Item1, base_pos.Item2) < 2) { if(GameBoard.distance(enemy_unit.xPos, enemy_unit.yPos, unit.xPos, unit.yPos) < 2) { return HEURISTIC_ATTACK_BASE_ATTACKER; } else { return 0; } } else { return 0; } } return 0; }