More lib files, more functional

Still missing that 'attack' fn, though :(
This commit is contained in:
texhno 2023-12-06 01:21:48 +01:00
parent 42b8afdecf
commit 545f8b4366
4 changed files with 43 additions and 15 deletions

View File

@ -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 { export interface ITank {
name: string; name: string;
@ -13,16 +15,15 @@ export const buildTank: BuildTank = (name) => ({
attackDelay: 0, attackDelay: 0,
} as ITank); } 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) => { const attack: Attack = (_acc, tank, _iTank, tanks) => {
// [TODO]: Refactor this imperative block tap(
if (tank.attackDelay === 0) { doIf(
const target = tanks.filter(isOther(tank.name))[Math.floor(Math.random() * tanks.length)]; isReadyToAttack,
const attackDamage = critMultiplier(tank.health) * tank.health / 100; dealDamageToRandom(tanks)
target.health -= attackDamage; )
}; )(tank);
return mapR(setProp("attackDelay", tank))(tanks);
return tanks.map(setStats);
}; };
type Attacks = (tanks: ITank[]) => ITank[]; type Attacks = (tanks: ITank[]) => ITank[];

7
lib/combinators.ts Normal file
View File

@ -0,0 +1,7 @@
export const tap = (fn) => (input) => {
fn(input);
return input;
};
export const doIf = (fn, pred) => (input) =>
pred ? fn(input) : input;

View File

@ -1,17 +1,27 @@
import { ITank } from "./Tank"; import { ITank } from "./Tank";
import { filterR, pipe } from "./reducers";
type CritMultiplier = (health: number) => number; type CritMultiplier = (health: number) => number;
export const critMultiplier: CritMultiplier = (health) => export const critMultiplier: CritMultiplier = (health) =>
Math.floor(Math.random() * 10) >= 10 - health / 10 ? 1 : 2; Math.floor(Math.random() * 10) >= 10 - health / 10 ? 1 : 2;
type SetStats = (tank: ITank) => ITank; type PickRandom = <T>(arr: T[]) => T;
export const setStats: SetStats = (tank) => ({ export const pickRandom: PickRandom = (arr) =>
...tank, arr[Math.floor(Math.random() * arr.length)];
attackDelay: tank.attackDelay === 0 ? Math.floor(tank.health / 10) : tank.attackDelay - 1,
} as ITank); export const setProp = (prop, val) => (obj) => obj[prop] = val;
type IsLive = (tank: ITank) => boolean; type IsLive = (tank: ITank) => boolean;
export const isLive: IsLive = (tank) => tank.health >= 0; export const isLive: IsLive = (tank) => tank.health >= 0;
type IsOther = (name: string) => (tank: ITank) => boolean; type IsOther = (name: string) => (tank: ITank) => boolean;
export const isOther: IsOther = (name) => (tank) => tank.name !== name; 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);
};

10
lib/reducers.ts Normal file
View File

@ -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 ,[]);