Skip to content

Commit

Permalink
feat: prevent small spaces
Browse files Browse the repository at this point in the history
  • Loading branch information
DJCrossman committed Dec 6, 2023
1 parent c75cbd9 commit 4c96484
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 26 deletions.
8 changes: 8 additions & 0 deletions src/app.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ export class AppService {
this.history = [];
}

isSnakeDead(): boolean {
const [previous, current] = this.history.slice(-2)
return previous && current && previous.board.snakes.length > current.board.snakes.length
}

public async getSnake(): Promise<Personalization> {
Logger.log(`Getting snake...`, 'AppService');
return {
Expand All @@ -24,11 +29,14 @@ export class AppService {

public async start(state: GameState): Promise<void> {
Logger.log(`Starting [${state.game.id}]...`, 'AppService');
this.history = []
}

public async move(state: GameState): Promise<SnakeCommand> {
this.history.push(state);
Logger.log(`Staring turn [${state.turn}]...`, 'AppService');
let shout: string
if (this.isSnakeDead()) shout = 'f'
const { move } = await this.moveService.findMove(state, 1);
Logger.log(`Moving [${move}] on turn ${state.turn}`, 'AppService');
return {
Expand Down
90 changes: 64 additions & 26 deletions src/movement/movement.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import { MoveRank } from '../dtos/move-rank';
export class MovementService {
random: boolean = true;

private foodWeight = 0.8
private conflictWeight = 0.4
private defaultWeight = 0.5
private foodWeight = 0.8;
private conflictWeight = 0.4;
private defaultWeight = 0.5;

constructor(private boundaryService: BoundaryService) {}
constructor(private boundaryService: BoundaryService) { }

async calculateWeight(
state: GameState,
Expand All @@ -22,77 +22,115 @@ export class MovementService {
const options: Direction[] = this.random
? Object.values(DirectionEnum).sort(() => Math.random() - 0.5)
: Object.values(DirectionEnum);
const newState: GameState = this.boundaryService.moveAsState(move, state)
if (weight < (1 / (state.board.height * 20))) {
const newState: GameState = this.boundaryService.moveAsState(move, state);

// Prioritize open spaces by checking the number of available neighboring spaces
const openSpaceWeight = 1 / newState.you.body.length;

// Penalize moves that lead to narrow paths or dead ends
const narrowPathPenalty = 0.2;

if (weight < openSpaceWeight) {
return weight;
}

let moves: MoveRank[];
const isOnFoodSource = this.boundaryService.withinSet(
state.board.food,
newState.you.head,
);
const hasConflictPotential = (await Promise.all(newState.board.snakes.map((snake: Snake): boolean => {
if (snake.id === newState.you.id) return false
const snakeMoves: Coordinate[] = options.map((m: Direction) => this.boundaryService.moveAsCoord(m, snake.head))
const isSmallerSnake = snake.body.length > newState.you.body.length
return isSmallerSnake && this.boundaryService.withinSet(
snakeMoves,
newState.you.head,
let conflictAvoidanceWeight = 0;
const hasConflictPotential = (
await Promise.all(
newState.board.snakes.map(async (snake: Snake): Promise<boolean> => {
if (snake.id === newState.you.id) return false;

const snakeMoves: Coordinate[] = options.map((m: Direction) =>
this.boundaryService.moveAsCoord(m, snake.head),
);

// Dynamic conflict avoidance: Increase weight for conflict avoidance if a smaller snake is close
const isSmallerSnake = snake.body.length > newState.you.body.length;
conflictAvoidanceWeight = isSmallerSnake
? this.conflictWeight
: 0;

return (
isSmallerSnake &&
this.boundaryService.withinSet(snakeMoves, newState.you.head)
);
}),
)
}))).some(m => m)
const isHungry = state.you.health < 70
).some((m) => m);

const isHungry = state.you.health < 70;

if (hasConflictPotential && !isHungry) {
moves = await Promise.all(
options.map(m =>
options.map((m) =>
this.calculateMove(
newState,
m,
weight * this.conflictWeight,
weight * (this.conflictWeight + openSpaceWeight),
),
),
);
} else if (isOnFoodSource) {
moves = await Promise.all(
options.map(m =>
options.map((m) =>
this.calculateMove(
newState,
m,
weight * this.foodWeight,
weight * (this.foodWeight + openSpaceWeight),
),
),
);
} else {
moves = await Promise.all(
options.map(m =>
options.map((m) =>
this.calculateMove(
newState,
m,
weight * this.defaultWeight,
weight * (this.defaultWeight + openSpaceWeight - narrowPathPenalty),
),
),
);
}

const projectedWeight =
moves.reduce((a, b) => a + b.weight, 0) / moves.length;
return projectedWeight + (weight * this.defaultWeight);

// Adjust weight by incorporating open space weight and conflict avoidance weight
return projectedWeight + (weight * openSpaceWeight - conflictAvoidanceWeight);
}

async calculateMove(
state: GameState,
move: Direction,
weight: number,
): Promise<MoveRank> {
const newState: GameState = this.boundaryService.moveAsState(move, state)
const isOffTheBoard = this.boundaryService.offBoard(state, newState.you.head);
const newState: GameState = this.boundaryService.moveAsState(
move,
state,
);

const isOffTheBoard = this.boundaryService.offBoard(
state,
newState.you.head,
);
const isWithinOwnBody = this.boundaryService.withinSet(
newState.you.body.slice(1, newState.you.body.length),
newState.you.head,
);
const isWithinAnotherSnake = state.board.snakes.some(s => this.boundaryService.withinSet(s.body, newState.you.head))
const isWithinAnotherSnake = state.board.snakes.some((s) =>
this.boundaryService.withinSet(s.body, newState.you.head),
);

if (!isOffTheBoard && !isWithinOwnBody && !isWithinAnotherSnake) {
const _weight = await this.calculateWeight(state, move, weight);
return { move, weight: _weight };
}

return { move, weight: 0 };
}

Expand All @@ -101,7 +139,7 @@ export class MovementService {
? Object.values(DirectionEnum).sort(() => Math.random() - 0.5)
: Object.values(DirectionEnum);
const moves: MoveRank[] = await Promise.all(
options.map(move => this.calculateMove(state, move, weight)),
options.map((move) => this.calculateMove(state, move, weight)),
);
return moves.reduce((a, b) => (a.weight >= b.weight ? a : b), {
move: 'right',
Expand Down

0 comments on commit 4c96484

Please sign in to comment.