diff --git a/.eslintrc.js b/.eslintrc.js index 4abdb21..e60c2fb 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -31,6 +31,7 @@ module.exports = { // note you must disable the base rule as it can report incorrect errors 'no-unused-vars': 'off', + 'array-element-newline': 'off', '@typescript-eslint/no-unused-vars': [ 'error', { diff --git a/src/Collaborate.ts b/src/Collaborate.ts new file mode 100644 index 0000000..f03979b --- /dev/null +++ b/src/Collaborate.ts @@ -0,0 +1,33 @@ +import { Behavior, Directions, ISnake } from './Types'; +import Pathfinder from './Pathfinder'; + +export function collaborate( + action: Behavior, // eslint-disable-next-line @typescript-eslint/no-explicit-any + actionArguments: Array, + us: ISnake, + partners: ISnake[] +): () => Directions { + return (): Directions => { + const pathfinder = actionArguments.find(arg => arg instanceof Pathfinder); + partners = partners + .sort((s1, s2) => (s1.id > s2.id ? 1 : -1)) + .filter(s1 => s1.id < us.id); + partners.forEach(snake => { + const snakeArgs = actionArguments.map(arg => (arg === us ? snake : arg)); + const direction = action(...snakeArgs); + const nonWalkableCoordinate = snake.body[0]; + switch (direction) { + case Directions.UP: + nonWalkableCoordinate.y -= 1; + case Directions.DOWN: + nonWalkableCoordinate.y += 1; + case Directions.LEFT: + nonWalkableCoordinate.x -= 1; + case Directions.RIGHT: + nonWalkableCoordinate.x += 1; + } + pathfinder.grid[nonWalkableCoordinate.x][nonWalkableCoordinate.y] = 1; + }); + return action(...actionArguments); + }; +} diff --git a/src/SnakeBrain.ts b/src/SnakeBrain.ts index 25176ef..f8c6ebd 100644 --- a/src/SnakeBrain.ts +++ b/src/SnakeBrain.ts @@ -1,7 +1,8 @@ -import { IGameState, ISnake, IBoard, IGame, Directions } from './Types'; +import { IGameState, ISnake, IBoard, Directions } from './Types'; import { turtle } from './behaviours/turtle'; import { canKillNemesis, + getPartners, getNemesis, shouldChaseOurTail, firstToFood, @@ -12,13 +13,14 @@ import { chaseEnemyTail } from './behaviours/chaseEnemyTail'; import Pathfinder from './Pathfinder'; import { floodFill } from './behaviours/floodFill'; import seekSafestFood from './behaviours/seekSafestFood'; +import { collaborate } from './Collaborate'; // I hate writing "this." all the time. -let game: IGame; let turn: number; let board: IBoard; let selfDestruct: boolean; let us: ISnake; +let partners: ISnake[]; let nemesis: ISnake; let everybody: ISnake[]; @@ -26,7 +28,6 @@ export default class SnakeBrain { private action: Directions; constructor(gameStateResponse: IGameState, exploited: boolean) { - game = gameStateResponse.game; turn = gameStateResponse.turn; board = gameStateResponse.board; // Not sure why we left this in here? @@ -34,6 +35,7 @@ export default class SnakeBrain { selfDestruct = exploited; us = gameStateResponse.you; everybody = board.snakes; + partners = getPartners(us, everybody); nemesis = getNemesis(us, everybody); } @@ -44,37 +46,51 @@ export default class SnakeBrain { */ public decide(): SnakeBrain { // Logic for start of game. - // eslint-disable-next-line array-element-newline - console.log({ turn, game, board, us }); + // console.log({ turn, game, board, us }); // Instantiate Pathfinder with board and snakes const PF = new Pathfinder(board, everybody); // Try some moves out, see what feels good const cower = turtle(PF, us); - const headbutt = attackHead(PF, us, nemesis); - const goingInCircles = chaseTail(PF, us); - const hangry = seekSafestFood(PF, board, us); - const ridingCoattails = chaseEnemyTail(PF, us, everybody); - + const headbutt = collaborate( + attackHead, + [new Pathfinder(board, everybody), us, nemesis], + us, + partners + ); + const goingInCircles = collaborate( + chaseTail, + [new Pathfinder(board, everybody), us], + us, + partners + ); + const hangry = collaborate( + seekSafestFood, + [new Pathfinder(board, everybody), board, us], + us, + partners + ); + const ridingCoattails = (): Directions => chaseEnemyTail(PF, us, everybody); + let action: Directions; if (selfDestruct) { // OH NO! We've been hacked! - console.log('AHHHHHH'); + console.log(`${turn} | ${us.id}: AHHHHHH`); this.action = cower; - } else if (canKillNemesis(us, everybody) && headbutt) { - console.log('*THUNK*'); - this.action = headbutt; - } else if (firstToFood && hangry) { - console.log('CHONK'); - this.action = hangry; - } else if (goingInCircles && shouldChaseOurTail(us, turn)) { - console.log('Follow our butt'); - this.action = goingInCircles; - } else if (ridingCoattails) { - console.log('Follow your butt'); - this.action = ridingCoattails; + } else if (canKillNemesis(us, everybody) && (action = headbutt())) { + console.log(`${turn} | ${us.id}: *THUNK*`); + this.action = action; + } else if (firstToFood && (action = hangry())) { + console.log(`${turn} | ${us.id}: CHONK`); + this.action = action; + } else if (shouldChaseOurTail(us, turn) && (action = goingInCircles())) { + console.log(`${turn} | ${us.id}: Follow our butt`); + this.action = action; + } else if ((action = ridingCoattails())) { + console.log(`${turn} | ${us.id}: Follow your butt`); + this.action = action; } else { - console.log('Feeling aimless'); + console.log(`${turn} | ${us.id}: Feeling aimless`); // floodFill is costly, so only calculating when we need it this.action = PF.getDirection(us.body[0], floodFill(PF.grid, us)); } diff --git a/src/Types.ts b/src/Types.ts index bf85f9e..fbec53c 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -37,3 +37,6 @@ export enum Directions { UP = 'up', DOWN = 'down', } + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type Behavior = (...args: any[]) => Directions; diff --git a/src/helpers.ts b/src/helpers.ts index 25e7110..0b69ebf 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -11,6 +11,16 @@ export function getNemesis(us: ISnake, snakes: ISnake[]): ISnake { return snakes.find(snake => snake.name !== us.name); } +/** + * Get our partners in crime from the array of snakes + * @param {ISnake} us - this instance of Echosnek + * @param {ISnake[]} snakes - all the snakes + */ +export function getPartners(us: ISnake, snakes: ISnake[]): ISnake[] { + // There should only ever be 1 snake that has a different name + return snakes.filter(snake => snake.name === us.name && snake.id !== us.id); +} + /** * Are we currently equal in length or longer than the enemy? * @param {ISnake} us - this instance of Echosnek diff --git a/src/index.ts b/src/index.ts index 643d27d..15c65fb 100755 --- a/src/index.ts +++ b/src/index.ts @@ -25,7 +25,7 @@ app.use(poweredByHandler); // Handle POST request to '/start' app.post('/start', (request, response) => { // NOTE: Do something here to start the game - console.log(request.body); + // console.log(request.body); // Response data const data = { @@ -53,14 +53,14 @@ app.post('/move', (request, response) => { }); app.post('/end', (request, response) => { - console.log(request.body); - + // console.log(request.body); + console.log('/end'); // NOTE: Any cleanup when a game is complete. return response.json({}); }); app.post('/ping', (request, response) => { - console.log(request.body); + // console.log(request.body); // Used for checking if this snake is still alive. return response.json({});