diff --git a/lib/Tank.ts b/lib/Tank.ts index 5cf0635..2a1b3ea 100644 --- a/lib/Tank.ts +++ b/lib/Tank.ts @@ -1,4 +1,6 @@ -import { setStats, isLive, isOther, critMultiplier } from "./helpers"; +import { doIf, tap } from "./combinators"; +import { isLive, setProp, dealDamageToRandom, isReadyToAttack } from "./helpers"; +import { mapR, pipe } from "./reducers"; export interface ITank { name: string; @@ -13,16 +15,15 @@ export const buildTank: BuildTank = (name) => ({ attackDelay: 0, } as ITank); -type Attack = (_: ITank[], tank: ITank, tIndex: number, tanks: ITank[]) => ITank[]; +type Attack = (_prevTanks: ITank[], tank: ITank, tIndex: number, tanks: ITank[]) => ITank[]; const attack: Attack = (_acc, tank, _iTank, tanks) => { - // [TODO]: Refactor this imperative block - if (tank.attackDelay === 0) { - const target = tanks.filter(isOther(tank.name))[Math.floor(Math.random() * tanks.length)]; - const attackDamage = critMultiplier(tank.health) * tank.health / 100; - target.health -= attackDamage; - }; - - return tanks.map(setStats); + tap( + doIf( + isReadyToAttack, + dealDamageToRandom(tanks) + ) + )(tank); + return mapR(setProp("attackDelay", tank))(tanks); }; type Attacks = (tanks: ITank[]) => ITank[]; diff --git a/lib/combinators.ts b/lib/combinators.ts new file mode 100644 index 0000000..4b857d1 --- /dev/null +++ b/lib/combinators.ts @@ -0,0 +1,7 @@ +export const tap = (fn) => (input) => { + fn(input); + return input; +}; + +export const doIf = (fn, pred) => (input) => + pred ? fn(input) : input; diff --git a/lib/helpers.ts b/lib/helpers.ts index e6cb4ed..aadd25d 100644 --- a/lib/helpers.ts +++ b/lib/helpers.ts @@ -1,17 +1,27 @@ import { ITank } from "./Tank"; +import { filterR, pipe } from "./reducers"; type CritMultiplier = (health: number) => number; export const critMultiplier: CritMultiplier = (health) => Math.floor(Math.random() * 10) >= 10 - health / 10 ? 1 : 2; -type SetStats = (tank: ITank) => ITank; -export const setStats: SetStats = (tank) => ({ - ...tank, - attackDelay: tank.attackDelay === 0 ? Math.floor(tank.health / 10) : tank.attackDelay - 1, -} as ITank); +type PickRandom = (arr: T[]) => T; +export const pickRandom: PickRandom = (arr) => + arr[Math.floor(Math.random() * arr.length)]; + +export const setProp = (prop, val) => (obj) => obj[prop] = val; type IsLive = (tank: ITank) => boolean; export const isLive: IsLive = (tank) => tank.health >= 0; type IsOther = (name: string) => (tank: ITank) => boolean; export const isOther: IsOther = (name) => (tank) => tank.name !== name; + +export const isReadyToAttack = (tank: ITank) => tank.attackDelay === 0; + +type DealDamageToRandom = (tanks: ITank[]) => (tank: ITank) => void; +export const dealDamageToRandom: DealDamageToRandom = (tanks) => (tank) => { + const target = pipe(filterR(isOther(tank.name)), pickRandom)(tanks); + const attackDamage = critMultiplier(tank.health) * tank.health / 100; + setProp("health", target.health - attackDamage)(target); +}; diff --git a/lib/reducers.ts b/lib/reducers.ts new file mode 100644 index 0000000..5409002 --- /dev/null +++ b/lib/reducers.ts @@ -0,0 +1,10 @@ +export const pipe = (...fns) => (input) => + fns.reduce((acc, fn) => fn(acc), input); + +export const redFn = (acc, x) => [...acc, x]; + +export const mapR = (fn) => (input) => + input.reduce((acc, x) => [...acc, fn(x)], []); + +export const filterR = (pred) => (input) => + input.reduce((acc, x) => pred(x) ? [...acc, x] : acc ,[]);