Robot Rumble ALPHA
discord
try it!
boards
tutorial
docs
login
/
signup
class IntentRegistry { constructor() { this.commands = []; } register(robot, action, coordsOrDirection) { const coords = (coordsOrDirection instanceof Coords) ? coordsOrDirection : robot.coords.add(coordsOrDirection); this.commands.unshift({ robot, action, coords, }); } commandForCoords(coords) { return this.commands .filter((c) => c.coords.toString() === coords.toString())[0]; } commandForRobot(robot) { return this.commands .filter((c) => c.robot.id === robot.id)[0]; } coordHasPlannedMovement(coords) { return !(typeof this.commandForCoords(coords) === 'undefined'); } } function cartesianProduct(arrA, arrB) { return [].concat(...arrA.map((a) => [].concat(arrB.map((b) => [a, b])))); } function generateMoves() { const directions = [ Direction.North, Direction.East, Direction.South, Direction.West ]; const actions = [ ActionType.Attack, ActionType.Move, ]; return cartesianProduct(directions, actions) .map(([direction, action]) => ({ direction, action })); } class EnhancedState { constructor(innerState) { this.innerState = innerState; } get enemyRobots() { return this.innerState .objsByTeam(this.innerState.otherTeam) .filter((o) => o.objType === ObjType.Unit); } ; get teamRobots() { return this.innerState .objsByTeam(this.innerState.ourTeam) .filter((o) => o.objType === ObjType.Unit); } ; weakerEnemiesThan(me) { return this.enemyRobots.filter((o) => o.health < me.health); } ; objectsSurrounding(coords) { return [ this.innerState.objByCoords(coords.add(Direction.North)), this.innerState.objByCoords(coords.add(Direction.East)), this.innerState.objByCoords(coords.add(Direction.South)), this.innerState.objByCoords(coords.add(Direction.West)), ]; } } function directionToCoord(direction) { if (direction === Direction.North) return new Coords(0, -1); if (direction === Direction.South) return new Coords(0, 1); return direction.toCoords; } function resolveNewPosition(option, turnInfo) { if (option.action === ActionType.Attack) return turnInfo.me.coords; const potentialNewPosition = turnInfo.me.coords.add(directionToCoord(option.direction)); if (turnInfo.intentRegistry.coordHasPlannedMovement(potentialNewPosition)) return turnInfo.me.coords; if (turnInfo.state.objByCoords(potentialNewPosition)) return turnInfo.me.coords; return potentialNewPosition; } // TODO Remove before release, rumblebot is broken function myCoordsToRealCoords(coords) { return coords; return new Coords(coords.y, coords.x); } function resolveAttackTarget(option, turnInfo) { if (option.action !== ActionType.Attack) return null; const attackedCoord = turnInfo.me.coords.add(directionToCoord(option.direction)); const attackedObj = turnInfo.state.objByCoords(myCoordsToRealCoords(attackedCoord)) || null; if (attackedObj && attackedObj.objType === ObjType.Terrain) return null; return attackedObj; } const MAX_HEALTH = 5; function scoreNearness(coords, set, includeHealth) { return 1 / (set.reduce((accum, o) => accum + (coords.distanceTo(o.coords) * (includeHealth ? o.health / MAX_HEALTH : 1)), 0) / (set.length * (includeHealth ? MAX_HEALTH : 1))); } function getScore(option, turnInfo) { const enhancedState = new EnhancedState(turnInfo.state); // Information about movement const newPosition = resolveNewPosition(option, turnInfo); const friendsDistance = scoreNearness(newPosition, enhancedState.teamRobots, false); const enemiesDistance = scoreNearness(newPosition, enhancedState.enemyRobots, false); const weakEnemiesDistance = scoreNearness(newPosition, enhancedState.enemyRobots, true); // Attack scoring const attackTarget = resolveAttackTarget(option, turnInfo); const othersAttacking = !attackTarget ? 0 : enhancedState .objectsSurrounding(attackTarget.coords) .filter((o) => o).length; const attackingEnemiesHp = enhancedState.objectsSurrounding(turnInfo.me.coords) .filter((o) => o && o.team === turnInfo.state.otherTeam) .reduce((accum, o) => accum + o.health, 0); if (option.action === ActionType.Attack) { if (!attackTarget || attackTarget.team !== turnInfo.state.otherTeam) return -Infinity; return (1 + turnInfo.me.health + othersAttacking - attackingEnemiesHp); } // Movement scoring if (newPosition.toString() === turnInfo.me.coords.toString()) { return -Infinity; } if (turnInfo.me.health === 1) { return 1 / enemiesDistance; } else { return weakEnemiesDistance * 2; } } function scoreMove(option, turnInfo) { return { score: getScore(option, turnInfo), moveOption: option, }; } let monitorId; let intents; function initTurn(state) { intents = new IntentRegistry(); } function robot(state, unit) { if (!monitorId || !state.objById(monitorId)) { monitorId = unit.id; } const turnInfo = { me: unit, state, intentRegistry: intents }; const moves = generateMoves() .map((move) => scoreMove(move, turnInfo)) .filter((move) => move.score !== null) .sort((a, b) => b.score - a.score); if (moves.length === 0) return null; const move = moves[0]; if (monitorId === unit.id) { debug.inspect(unit); console.log(JSON.stringify(moves)); } debug.log('moves considered', moves.length); debug.log('winning score', move.score); debug.log('winning move', JSON.stringify(move.moveOption)); debug.log('expected target', resolveAttackTarget(move.moveOption, turnInfo)); debug.log('expected position', resolveNewPosition(move.moveOption, turnInfo)); if (typeof move !== 'undefined') { return new Action(move.moveOption.action, move.moveOption.direction); } return null; }
Made with <3 by Anton and Noa
github org