// CONSTANTS
let _myCyborgs; let _enemyCyborgs; let _myProd; let _enemyProd;
///PARSEAR READLINE
var _factoryCount = parseInt(readline()); var _factories = {}; var _myFactories, _enemyFactories, _neutralFactories, _maxEta;
var _distances = { 'get': function( id1, id2 ){ if( id1 < id2 ){ return this[id1][id2]; } if( id1 > id2 ){ return this[id2][id1]; } return 0; } }; var _maxDistance = 0; for( var i=0; i < _factoryCount; i++ ){ _distances[i] = {}; }
var linkCount = parseInt(readline()); for (var i = 0; i < linkCount; i++) { var inputs = readline().split(' '); var f1 = parseInt(inputs[0]); var f2 = parseInt(inputs[1]); var distance = parseInt(inputs[2]); _distances[f1][f2] = distance;
if( distance > _maxDistance ){ _maxDistance = distance; }
}
var _bombCount = 2; var _enemyBombs, _enemyBombedFactories; var _enemyBombLive = { };
/// MAIN LOOP
while (true) {
// CONSTANTS LOOP
_myCyborgs = 0;
_enemyCyborgs = 0;
_myProd = 0;
_enemyProd = 0;
_myFactories = [];
_enemyFactories = [];
_neutralFactories = [];
_enemyBombs = [];
_enemyBombedFactories = [];
_maxEta = 0;
// READLINE LOOP
var entityCount = parseInt(readline());
for( var i = 0; i < entityCount; i++ ){
var inputs = readline().split(' ');
var entityId = parseInt(inputs[0]);
var entityType = inputs[1];
var arg1 = parseInt(inputs[2]);
var arg2 = parseInt(inputs[3]);
var arg3 = parseInt(inputs[4]);
var arg4 = parseInt(inputs[5]);
var arg5 = parseInt(inputs[6]);
// FACTORY
if( entityType === 'FACTORY' ){
var f = {
'id': entityId,
'cyborgs': arg2,
'owner': arg1,
'production': arg3,
'tbnp': arg4
};
if( f.owner === 1 ){
_myFactories.push( f.id );
_myCyborgs += f.cyborgs;
_myProd += f.production;
}
else if( f.owner === -1 ){
_enemyFactories.push( f.id );
_enemyCyborgs += f.cyborgs;
_enemyProd += f.production;
}
else {
_neutralFactories.push( f.id );
}
// TIPO
f.incomings = {
'allies': {
'max': 0
},
'enemies': {
'max': 0
},
'add': function( turns, number, ally ){
var sub = ally ? 'allies' : 'enemies';
if( !this[sub][turns] ){
this[sub][turns] = 0;
}
this[sub][turns] += number;
if( turns > this[sub]['max'] ){
this[sub]['max'] = turns;
}
if( turns > _maxEta ){
_maxEta = turns;
}
}
};
_factories[entityId] = f;
}
/// LOGICA TROOP
else if( entityType === 'TROOP' ){
var t = {
'owner': arg1,
'src': arg2,
'dest': arg3,
'cyborgs': arg4,
'eta': arg5
};
var destFactory = _factories[arg3];
destFactory.incomings.add( t.eta, t.cyborgs, t.owner === 1 );
if( t.owner === 1 ){
_myCyborgs += t.cyborgs;
}
else if( t.owner === -1 ){
_enemyCyborgs += t.cyborgs;
}
}
// LOOP BOMBO
else {
if( arg1 === -1 ){
if( _enemyBombLive[entityId] !== undefined ){
_enemyBombLive[entityId]++;
}
else {
_enemyBombLive[entityId] = 1;
}
_enemyBombs.push({
'id': entityId,
'source': arg2,
'liveTime': _enemyBombLive[entityId]
});
}
else {
_enemyBombedFactories.push( arg3 );
}
}
}
// DISTANCIA FACTORY
for( var i=0; i < _factoryCount; i++ ){
var f = _factories[i];
var avg = 0;
for( var j=0; j < _myFactories.length; j++ ){
avg += _distances.get( f.id, _myFactories[j] );
}
f.avgDist = avg;
}
var possibleActions = [];
var simulation = {};
var count = 1;
var simFactories = clone( _factories );
while( count <= _maxDistance ){
for( var i=0; i < _factoryCount; i++ ){
var f = simFactories[i];
// ACTUALITZAR INFO ALIATS
updateIncomings( f.incomings.allies );
// ACTUALITZAR INFO ENEMICS
updateIncomings( f.incomings.enemies );
if( f.owner !== 0 ){
f.cyborgs += f.production;
}
// ME PETE :(
var incAllies = f.incomings.allies[0] ? f.incomings.allies[0] : 0;
var incEnemies = f.incomings.enemies[0] ? f.incomings.enemies[0] : 0;
if( f.owner === 1 ){
var result = f.cyborgs - ( incEnemies - incAllies );
if( result < 0 ){
f.owner = -1;
f.cyborgs = Math.abs( result );
// EXECUCOO ACTIONS
possibleActions.push({
'name': 'support ally',
'actionFactory': undefined,
'targetFactory': f.id,
'cyborgs': f.cyborgs,
'turnsEffect': count - 1,
'score': actionScore( f.production, f.cyborgs, f.avgDist )
});
} else {
f.cyborgs = result;
}
}
else if( f.owner === -1 ){
var result = f.cyborgs - ( incAllies - incEnemies );
if( result < 0 ){
f.owner = 1;
f.cyborgs = Math.abs( result );
} else {
f.cyborgs = result;
possibleActions.push({
'name': 'attack enemy',
'actionFactory': undefined,
'targetFactory': f.id,
'cyborgs': f.cyborgs + f.production + 1,
'turnsEffect': count - 1,
'score': actionScore( f.production, f.cyborgs + f.production + 1, f.avgDist )
});
}
}
else {
var result = f.cyborgs - Math.abs( incAllies - incEnemies );
if( result < 0 && incAllies > incEnemies ){
f.owner = 1;
f.cyborgs = Math.abs( result );
}
else if( result < 0 ){
f.owner = -1;
f.cyborgs = Math.abs( result );
possibleActions.push({
'name': 'attack enemy',
'actionFactory': undefined,
'targetFactory': f.id,
'cyborgs': f.cyborgs + 1,
'turnsEffect': count - 1,
'score': actionScore( f.production, f.cyborgs + 1, f.avgDist )
});
}
else {
f.cyborgs = result;
possibleActions.push({
'name': 'capture neutral',
'actionFactory': undefined,
'targetFactory': f.id,
'cyborgs': f.cyborgs + 1,
'turnsEffect': count - 1,
'score': actionScore( f.production, f.cyborgs + 1, f.avgDist )
});
}
}
}
simulation[count] = clone( simFactories );
count++;
}
for( var i=0; i < _myFactories.length; i++ ){
var f = _factories[_myFactories[i]];
// INCOMING ME PETEN EL CUL
for( var j=0; j < _enemyBombs.length; j++ ){
var b = _enemyBombs[j];
var d = _distances.get( b.source, f.id );
if( b.liveTime === d ){
possibleActions.push({
'name': 'evacuate',
'actionFactory': f.id,
'targetFactory': undefined,
'cyborgs': f.cyborgs,
'score': actionScore( f.production, 0, 0 )
});
}
}
// ENVIAR BOMBA
if( _bombCount > 0 ){
for( var j=0; j < _enemyFactories.length; j++ ){
var ef = _factories[_enemyFactories[j]];
if( _enemyBombedFactories.indexOf( ef.id ) < 0 && ef.production > 0 ){
possibleActions.push({
'name': 'bomb enemy',
'actionFactory': undefined,
'targetFactory': ef.id,
'cyborgs': 0,
'score': actionScore( ef.production, 0, ef.avgDist )
});
}
}
}
// INCREMENTAR PRODUCCIO
if( ( _myCyborgs >= _enemyCyborgs + 10 || _myProd < _enemyProd )
&& f.production < 3 && f.cyborgs >= 10 ){
possibleActions.push({
'name': 'increase production',
'actionFactory': f.id,
'cyborgs': 10,
'score': actionScore( 1, 10, 0 )
});
}
}
possibleActions.sort( function(a, b){
if( a.score !== b.score ){
return b.score - a.score;
}
return a.turns - b.turns;
});
var actions = [];
var actionableFactories = {
'list': _myFactories.slice(),
'remove': function( id ){
var index = this.list.indexOf( id );
if( index >= 0 ){
this.list.splice( index, 1 );
}
}
}
var handledTargets = [];
while( actionableFactories.list.length > 0 && possibleActions.length > 0 ){
var a = possibleActions.shift();
// SWITCH ACTION EXECUCIO
switch( a.name ){
case 'attack enemy':
case 'capture neutral':
case 'support ally':
if( handledTargets.indexOf( a.targetFactory ) < 0 ){
var pActionFactories = actionableFactories.list.slice();
var iotf = pActionFactories.indexOf( a.targetFactory );
if( iotf >= 0 ){
pActionFactories.splice( iotf, 1 );
}
var actionsFactories = findActionFactories( pActionFactories, a.targetFactory, a.turnsEffect, a.cyborgs );
if( actionsFactories.length > 0 ){
for( var i=0; i < actionsFactories.length; i++ ){
var af = actionsFactories[i];
actions.push( move( af.id, a.targetFactory, af.cyborgs ) );
if( af.cyborgs === 0 ){
actionableFactories.remove( af.id );
}
}
handledTargets.push( a.targetFactory );
}
}
break;
// ENVIAR BOMBA ENEMIC
case 'bomb enemy':
var actionFactory = findClosestMinCyborgs( actionableFactories.list, a.targetFactory, 0 );
if( actionFactory >= 0 ){
actions.push( bomb( actionFactory, a.targetFactory ) );
actionableFactories.remove( actionFactory );
}
break;
// EVACUAR
case 'evacuate':
var myOtherFactories = _myFactories.slice();
myOtherFactories.splice( myOtherFactories.indexOf( a.actionFactory ), 1 );
var targetFactory = findClosest( myOtherFactories, a.actionFactory );
if( targetFactory >= 0 ){
actions.push( move( a.actionFactory, targetFactory, a.cyborgs ) );
actionableFactories.remove( a.actionFactory );
}
break;
//INCREMENTAR
case 'increase production':
actions.push( increaseProduction( a.actionFactory ) );
if( _factories[a.actionFactory].cyborgs === 0 ){
actionableFactories.remove( a.actionFactory );
}
break;
// SPLIT CYBORGS, NO ME FAN FALTA ALLA
case 'split cyborgs':
actions.push( move( a.actionFactory, a.targetFactory, a.cyborgs ) );
if( _factories[a.actionFactory].cyborgs === 0 ){
actionableFactories.remove( a.actionFactory );
}
break;
}
}
if( actions.length === 0 ){
actions.push('WAIT');
}
print( actions.join(';') );
}
//PUNTUAR function actionScore( productionGain, cyborgsEngaged, distance ){ var cybDen = cyborgsEngaged > 0 ? cyborgsEngaged : 1; var distDen = distance > 0 ? distance : 1; return productionGain / cybDen / distDen; }
// TROBAR FACTORY NECESARIES PER PEAR function findActionFactories( ids, from, maxDistance, cyborgsNeeded ){ var orderedIds = ids.sort( function(a, b){ var da = _distances.get(a, from); var db = _distances.get(b, from); if( da !== db ){ return da - db; } else { return _factories[b].cyborgs - _factories[a].cyborgs; } });
var found = [];
for( var i=0; i < orderedIds.length; i++ ){
var d = _distances.get( orderedIds[i], from );
if( d > maxDistance ){ break; }
var cybs = Math.min( cyborgsNeeded, _factories[orderedIds[i]].cyborgs );
if( cybs > 0 ){
found.push( { 'id': orderedIds[i], 'cyborgs': cybs } );
cyborgsNeeded -= cybs;
}
if( cyborgsNeeded <= 0 ){ break; }
}
return found;
}
// FIND MAX DISTANCE MINIM CYBORGS function findMaxDistanceMinCyborgs( list, from, maxDistance, minCyborgs ){ var orderedList = list.sort( function(a, b){ return _distances.get(b, from) - _distances.get(a, from); } ); for( var i=0; i < orderedList.length; i++ ){ var d = _distances.get( orderedList[i], from ); if( d <= maxDistance && _factories[orderedList[i]].cyborgs >= minCyborgs ){ return orderedList[i]; } } return -1; }
// TROBAR MES PROPER function findClosest( list, from ){ return findClosestMinCyborgs( list, from, 0 ); }
// EL MINIM AL QUE PODEM ATACAR function findClosestMinCyborgs( list, from, minCyborgs ){ var minDist = 100; var closest = -1; for( var i=0; i < list.length; i++ ){ var d = _distances.get( list[i], from ); if( d < minDist && _factories[list[i]].cyborgs >= minCyborgs ){ minDist = d; closest = list[i]; } } return closest; }
// ACTUALITZAR function updateIncomings( object ){ for( var j=0; j < object.max; j++ ){ if( object[j+1] ){ object[j] = object[j+1]; } } object[ object.max ] = undefined; if( object.max > 0 ){ object.max--; } }
// BOMB function bomb( src, dest ){ _bombCount--; return 'BOMB ' + src + ' ' + dest; }
// UP function increaseProduction( fid ){ _factories[fid].cyborgs -= 10; return 'INC ' + fid; }
//MOVE function move( src, dest, cyborgs ){ _factories[src].cyborgs -= cyborgs; return 'MOVE ' + src + ' ' + dest + ' ' + cyborgs; }
//DEP CLONE function clone( obj ){ return JSON.parse(JSON.stringify( obj )); }
// TO STRING DEBUG function stringify( obj ){ return JSON.stringify( obj, null, 2 ); }
Log in or sign up for Devpost to join the conversation.