import { Injectable } from '@angular/core' ; import { Store } from '@ngrx/store' ; import { Observable } from 'rxjs' ; import { of } from 'rxjs/observable/of' ; import 'rxjs/add/observable/forkJoin' ; import { fromPromise } from 'rxjs/observable/fromPromise' ; import { DataUnit } from '../../assets/units/data/data-unit.model' ; @ Injectable ( ) export class BattleAlgorithmService { constructor ( ) { } getTotalBattleOutcome ( rewards , attackerUnits , attackerQuantities , defenderUnits , defenderQuantities , defenderResources ) { const { defenderDeadQuantities , attackerDeadQuantities } = this . getBattleOutcome ( attackerUnits , attackerQuantities , defenderUnits , defenderQuantities ) ; const attackerRewards = this . getStealOutcome ( rewards , attackerUnits , attackerQuantities , defenderUnits , defenderQuantities , defenderResources ) ; const defenderRewards = this . getResourcesReward ( rewards , attackerUnits , attackerDeadQuantities , defenderUnits , defenderDeadQuantities ) ; return { attackerDeadQuantities , defenderDeadQuantities , attackerRewards , defenderRewards } ; } getBattleOutcome ( attackerUnits , attackerQuantities , defenderUnits , defenderQuantities ) { const attackerAttack = this . calculateAttack ( attackerUnits , attackerQuantities ) ; const attackerDefense = this . calculateDefense ( attackerUnits , attackerQuantities ) ; const defenderAttack = this . calculateAttack ( defenderUnits , defenderQuantities ) ; const defenderDefense = this . calculateDefense ( defenderUnits , defenderQuantities ) ; const trueDamageOfAttacker = ( attackerAttack - defenderDefense ) ; const trueDamageOfDefender = ( defenderAttack - attackerDefense ) ; const defenderDeadQuantities = this . damageUnits ( trueDamageOfAttacker , defenderUnits , defenderQuantities ) ; const attackerDeadQuantities = this . damageUnits ( trueDamageOfDefender , attackerUnits , attackerQuantities ) ; return { defenderDeadQuantities , attackerDeadQuantities } ; } getStealOutcome ( rewards , attackerUnits , attackerQuantities , defenderUnits , defenderQuantities , defenderResources ) { const rewardAttackerModifier = rewards . attacker ; const attackerAttack = this . calculateAttack ( attackerUnits , attackerQuantities ) ; const attackerDefense = this . calculateDefense ( attackerUnits , attackerQuantities ) ; const defenderAttack = this . calculateAttack ( defenderUnits , defenderQuantities ) ; const defenderDefense = this . calculateDefense ( defenderUnits , defenderQuantities ) ; let goldStolen = 0 ; let crystalStolen = 0 ; let knockedAttackerUnits = [ ] ; if ( defenderDefense < attackerAttack && ( defenderAttack < attackerDefense || defenderAttack === 0 ) ) { knockedAttackerUnits = this . knockUnits ( defenderAttack , attackerUnits , attackerQuantities ) ; const totalUnitsThatCanSteal = knockedAttackerUnits . map ( ( knockedAmount , i ) => ( attackerQuantities [ i ] - knockedAmount ) ) ; const attackerAttackAfterKnocked = this . calculateAttack ( attackerUnits , totalUnitsThatCanSteal ) ; const percentageStolen = Math . floor ( ( attackerAttackAfterKnocked * rewardAttackerModifier ) / attackerAttack ) ; goldStolen = ( percentageStolen * defenderResources [ 0 ] ) / 100 ; crystalStolen = ( percentageStolen * defenderResources [ 1 ] ) / 100 ; } return [ goldStolen , crystalStolen ] ; } getResourcesReward ( rewards , attackerUnits , attackerDeadQuantities , defenderUnits , defenderDeadQuantities ) { const rewardDefenderModifier = rewards . defender ; let goldReward = 0 ; let crystalReward = 0 ; for ( let i = 0 ; i < attackerUnits . length ; i ++ ) { if ( attackerUnits [ i ] . resource === 'gold' ) { goldReward += ( attackerUnits [ i ] . price * attackerDeadQuantities [ i ] ) ; } if ( attackerUnits [ i ] . resource === 'crystal' ) { crystalReward += ( attackerUnits [ i ] . price * attackerDeadQuantities [ i ] ) ; } } for ( let i = 0 ; i < defenderUnits . length ; i ++ ) { if ( defenderUnits [ i ] . resource === 'gold' ) { goldReward += ( defenderUnits [ i ] . price * defenderDeadQuantities [ i ] ) ; } if ( defenderUnits [ i ] . resource === 'crystal' ) { crystalReward += ( defenderUnits [ i ] . price * defenderDeadQuantities [ i ] ) ; } } goldReward = ( goldReward * rewardDefenderModifier / 100 ) ; crystalReward = ( crystalReward * rewardDefenderModifier / 100 ) ; return [ goldReward , crystalReward ] ; } calculateAttack ( units , quantities ) { let totalAttack = 0 ; units . forEach ( ( unit , i ) => { totalAttack += unit . attack * quantities [ i ] ; } ) ; return totalAttack ; } calculateDefense ( units , quantities ) { let totalDefense = 0 ; units . forEach ( ( unit , i ) => { totalDefense += unit . defense * quantities [ i ] ; } ) ; return totalDefense ; } damageUnits ( damage , units , quantities ) { let killedQuantities = quantities . map ( ( ) => 0 ) ; let zeroedTroops = 0 ; while ( damage > 0 && zeroedTroops !== quantities . length ) { const strongerUnitIndex = this . chooseIndexOfStrongerUnit ( units , quantities , killedQuantities ) ; const canKill = Math . floor ( damage / units [ strongerUnitIndex ] . health ) ; if ( canKill > 0 ) { killedQuantities [ strongerUnitIndex ] = ( canKill < quantities [ strongerUnitIndex ] ) ? canKill : quantities [ strongerUnitIndex ] ; if ( canKill < quantities [ strongerUnitIndex ] ) { damage = 0 ; } else { zeroedTroops += 1 ; damage -= ( killedQuantities [ strongerUnitIndex ] * units [ strongerUnitIndex ] . health ) ; } } else { damage = 0 ; } } return killedQuantities ; } knockUnits ( damage , units , quantities ) { let knockedQuantities = quantities . map ( ( ) => 0 ) ; let zeroedTroops = 0 ; while ( damage > 0 && zeroedTroops !== quantities . length ) { const strongerUnitIndex = this . chooseIndexOfStrongerUnit ( units , quantities , knockedQuantities ) ; const canKnock = Math . floor ( damage / units [ strongerUnitIndex ] . defense ) ; if ( canKnock > 0 ) { knockedQuantities [ strongerUnitIndex ] = ( canKnock < quantities [ strongerUnitIndex ] ) ? canKnock : quantities [ strongerUnitIndex ] ; if ( canKnock < quantities [ strongerUnitIndex ] ) { damage = 0 ; } else { zeroedTroops += 1 ; damage -= ( knockedQuantities [ strongerUnitIndex ] * units [ strongerUnitIndex ] . defense ) ; } } else { damage = 0 ; } } return knockedQuantities ; } chooseIndexOfStrongerUnit ( units , quantities , unavailableQuantities ) { let indexOfStrongerUnit = 0 ; let strongerDefense = - 1 ; for ( let i = 0 ; i < units . length ; i ++ ) { if ( quantities [ i ] - unavailableQuantities [ i ] > 0 && ( units [ i ] . defense > strongerDefense ) ) { indexOfStrongerUnit = i ; strongerDefense = units [ i ] . defense ; } } return indexOfStrongerUnit ; } }