Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Laser squad #69

Open
wants to merge 2 commits into
base: build-refactor
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AggroMiner/AggroMiner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Loadout: regen 3, thrusters 3, mine 1
* To be tougher, can substitute armor for thrusters or regen.
*/
const update = function() {
update = function() {
// Controls EWMA limits: for mine-only we want to keep this pretty tight
const TEAM_MIN_DIST = 2.5;
const TEAM_MAX_DIST = 6;
Expand Down
2 changes: 1 addition & 1 deletion ArtilleryMicro/ArtilleryMicro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Defense loadout: artillery 3, shield/reflect 3, thrusters 1
* Offense loadout: artillery 2, regen 3, thrusters 1, mine 1
*/
const update = function() {
update = function() {
// Controls whether artillery attack without sensors
const AGGRESSIVE = false;
// Controls EWMA limits: these can be a bit higher than for missiles
Expand Down
270 changes: 270 additions & 0 deletions LaserSquad/LaserSquad.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
/**
* Laser Squad
*
* A laser team that holds a diagonal formation in order to take advantage of
* the range and damage of lasers without the weaknesses. Can also lay mines.
*
* Example loadouts:
* - laser 3, shield 3, mine 1 (defensive)
* - laser 3, thrusters 3, mine 1 (fast mine carpet)
*/
type StuckSide = "edge_rear" | "edge_side";
type Order = Direction | StuckSide | "clear" | "active";

// Globals
declare let TEAM_SIZE, lastHBCount;

init = function() {
// Initialize the array.
saveOrder("active");
initHeartbeat();
TEAM_SIZE = 4;
debugLog("starting team size", TEAM_SIZE);
};

update = function() {
const SHIELD_RANGE = 5;

// Laser squad data has 3 states:
// - 'active': default state, lay mines or fire lasers.
// - 'clear': no enemies seen, can accept a move order.
// - 'edge_rear', 'edge_side': the bot is against a wall and cannot move
// back or sideways.
// - <direction>: one bot decided to move, so we all move in this direction.
//
// Rules:
// - Move orders are always followed first if received.
// - Reverse move orders can be issued by any bot, if no one on the team is
// stuck.
// - Forward move orders are only issued if all bots in the squad show
// 'clear':
//
// In default state, do the following:
// 1. Fire at visible enemies
// 2. Lay mines
// 3. Blind-fire lasers
const observedTeamSize = updateHeartbeat();
if (observedTeamSize < TEAM_SIZE) {
debugLog("seems someone died, team size", observedTeamSize);
resetTeamState();
}
TEAM_SIZE = observedTeamSize;

const order = getOrder();
let state: Order = "active";

// If we need to move to maintain formation, always do that first.
if (isDirection(order)) {
if (canMove(order)) {
saveOrder(state); // active
move(order);
}
} else {
// Only if we didn't try to process a directional move
// Check if we're against a wall
if (x == 0) {
state = "edge_rear";
saveOrder(state);
} else if (y == 0 || y == arenaHeight - 1) {
state = "edge_side";
saveOrder(state);
}
}

// Do we see anything nearby?
const closestEnemy = findEntity(
ENEMY,
ANYTHING,
SORT_BY_DISTANCE,
SORT_ASCENDING
);
if (!exists(closestEnemy)) {
if (!isStuck(state)) {
state = "clear";
saveOrder(state);
}
tryShieldFriendlyBots(SHIELD_RANGE);
tryLayMine();
// If we don't see anything and can activate sensors, go ahead.
// tryActivateSensors();
// Sensors and still don't see anything?
tryTeamMoveForward();

// If we still haven't moved at this point, just blind fire lasers.
blindFireLasers();
}

const closestEnemyBot = findEntity(
ENEMY,
BOT,
SORT_BY_DISTANCE,
SORT_ASCENDING
);
if (!exists(closestEnemyBot)) {
// TODO: set to 'clear' if the enemy chip or CPU is not in firing range.

// Not clear, chip or CPU.
tryShieldFriendlyBots(SHIELD_RANGE);
tryLayMine();
tryFireLasers();
blindFireLasers();
}

// Now we know there's a bot nearby.
if (!isStuck(state)) {
state = "active";
saveOrder(state);
}

const numEnemyBots = size(findEntities(ENEMY, BOT, false));
const enemyBotDistance = getDistanceTo(closestEnemyBot);

if (enemyBotDistance < 3.1 || numEnemyBots > 2) {
tryTeamMoveBack();
}

if (enemyBotDistance < 5.1) {
// Defenses
tryShieldFriendlyBots(SHIELD_RANGE);
tryReflect();
if (!isShielded()) tryShieldSelf();
// Weapons
tryFireLasers();
tryLayMine();
}

// Last resort moves
tryFireLasers();
tryLayMine();
blindFireLasers();
};

const initHeartbeat = function() {
if (sharedC == undefined) sharedC = 0;
sharedC = sharedC + 1;
lastHBCount = sharedC;
debugLog("starting count", lastHBCount);
};

const updateHeartbeat = function() {
sharedC = sharedC + 1;
const botsAlive = sharedC - lastHBCount;
lastHBCount = sharedC;
return botsAlive;
};

/**
* If all bots on the team show 'clear' or against edge, we can issue a move forward.
*/
const tryTeamMoveForward = function() {
// Load shared data
array1 = sharedD;
array2 = sharedE;

debugLog("trying forward", array1);

// Check if everyone can move forward.
let i = 0;
for (i = 0; i < size(array1); i++) {
const val = array1[i] as Order;
if (val !== "clear" && val !== "edge_rear" && val !== "edge_side") {
// We can't move forward
return;
}
}

debugLog("Starting team forward move");
// We can! Tell everyone to move forward.
const me = getEntityAt(x, y);
for (i = 0; i < size(array2); i++) {
// Continue statements not allowed!
if (array2[i] !== me) array1[i] = "forward";
}

// Save shared data
sharedD = array1;
sharedE = array2;
// Terminator
move("forward");
};

const tryTeamMoveBack = function() {
// Load shared data
array1 = sharedD;
array2 = sharedE;

// Check if everyone can move back. This basically means no one's already
// got a move order, or against the back. So everyone must be 'active',
// 'clear', or 'edge_side'.
let i = 0;
for (i = 0; i < size(array1); i++) {
const val = array1[i] as Order;
if (val !== "clear" && val !== "active" && val !== "edge_side") {
// We can't move backward
return;
}
}

debugLog("Starting team backward move");
// Tell everyone to move backward.
const me = getEntityAt(x, y);
for (i = 0; i < size(array2); i++) {
if (array2[i] !== me) array1[i] = "backward";
}

// Save shared data
sharedD = array1;
sharedE = array2;
// Terminator
move("backward");
};

/**
* Reset the movement states of potentially dead bots, so they don't mess up the
* coordination algorithm.
*/
const resetTeamState = function() {
// Load shared data
array1 = sharedD;
array2 = sharedE;

let i = 0;
for (i = 0; i < size(array1); i++) {
const val = array1[i] as Order;
if (val == "active") array1[i] = "clear";
}

// Save shared data
sharedD = array1;
sharedE = array2;
};

const getOrder = function(): Order {
return getData() as Order;
};

const saveOrder = function(order: Order) {
saveData(order);
};

const isDirection = function(order: Order): order is Direction {
return (
order == "up" ||
order == "down" ||
order == "left" ||
order == "right" ||
order == "forward" ||
order == "backward"
);
};

const isStuck = function(order: Order): order is StuckSide {
return order == "edge_rear" || order == "edge_side";
};

const blindFireLasers = function() {
// TODO: can improve this based on X position.
if (percentChance(50)) fireLasers("forward");
if (y > floor(arenaHeight / 2)) fireLasers("up");
else fireLasers("down");
};
7 changes: 7 additions & 0 deletions LaserSquad/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "../tsconfig-botland",
"compilerOptions": {
"outFile": "../build/LaserSquad.js"
},
"files": ["LaserSquad.ts"]
}
2 changes: 1 addition & 1 deletion MissileKite/MissileKite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* Missiles 3, thrusters 2, shield 2 is possible to use against enemies with
* high reflect and no thrusters (it saves 1 reflection).
*/
const update = function() {
update = function() {
const TEAM_MIN_DIST = 2.5;
const TEAM_MAX_DIST = 6.5;

Expand Down
2 changes: 1 addition & 1 deletion SmartMelee/SmartMelee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* Note that when using thrusters with zapper, damage is dealt every time the
* bot moves. So effective DPS can be twice as high (!!) as with normal zapper.
*/
const update = function() {
update = function() {
// Equip when we see someone, not blindly
const closestEnemyBot = findEntity(
ENEMY,
Expand Down
2 changes: 1 addition & 1 deletion Sneaktillery/Sneaktillery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* Sneaktillery will try to get into one of the positions that is 5 spaces away
* from the CPU and just unload until it has exploded.
*/
const update = function() {
update = function() {
// Whether we should use sensors (useful to shoot chips from afar)
const USE_SENSORS = false;
// Whether we should run if an enemy happens upon us and we have TP, to try
Expand Down
2 changes: 1 addition & 1 deletion ZapKite/ZapKite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* Turn 9: cloak (zap ends)
* Rinse and repeat.
*/
const update = function() {
update = function() {
// The first thing of chaos zapping is to never disrupt the cycle. Always
// get the counter and increment it before doing anything else.
const CYCLEN = 9;
Expand Down
8 changes: 8 additions & 0 deletions lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,14 @@ const tryFireMissiles = function() {
}
};

const tryFireLasers = function() {
if (willLasersHit()) {
const gank = findEntity(ENEMY, BOT, SORT_BY_LIFE, SORT_ASCENDING);
if (willLasersHit(gank)) fireLasers(gank);
fireLasers();
}
};

const tryFireArtillery = function() {
if (willArtilleryHit()) {
const gank = findEntity(ENEMY, BOT, SORT_BY_LIFE, SORT_ASCENDING);
Expand Down
3 changes: 3 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
{
"path": "./ArtilleryMicro"
},
{
"path": "./LaserSquad"
},
{
"path": "./MissileKite"
},
Expand Down
11 changes: 9 additions & 2 deletions types/bot-land/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,14 @@ declare type Direction =
| "forward"
| "backward";

// Terminators
/**
* Function called once each phase.
*/
declare let init: () => void;
/**
* Function called every turn.
*/
declare let update: () => void;

// Functions
declare function debugLog(...stuff: any[]): void;
Expand Down Expand Up @@ -97,7 +104,7 @@ declare function melee(entity?: Entity): void;
declare function willArtilleryHit(entity?: Entity): boolean;
declare function fireArtillery(entity?: Entity): void;
declare function willLasersHit(entity?: Entity): boolean;
declare function fireLasers(entity?: Entity): void;
declare function fireLasers(target?: Entity | Direction): void;
declare function willMissilesHit(entity?: Entity): boolean;
declare function fireMissiles(entity?: Entity): void;

Expand Down