Spec 01 four distinct ghosts #1
+47
-8
@@ -110,9 +110,52 @@ function movePacman( game ) {
|
|||||||
wrapTunnel( p, width );
|
wrapTunnel( p, width );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Celda situada `n` casillas por delante de Pac-Man segun su direccion.
|
||||||
|
// Las columnas se mantienen dentro del laberinto con clamp; si el offset
|
||||||
|
// se sale lateralmente por el tunel, la columna se ancla a la de Pac-Man.
|
||||||
|
function aheadOfPacman( game, n ) {
|
||||||
|
const p = game.pacman;
|
||||||
|
const W = game.grid[ 0 ].length;
|
||||||
|
const d = DIRS[ p.dir ] || { x: 0, y: 0 };
|
||||||
|
const px = Math.round( p.x );
|
||||||
|
const py = Math.round( p.y );
|
||||||
|
const ty = py + d.y * n;
|
||||||
|
if ( ty === TUNNEL_ROW ) return { x: px, y: ty };
|
||||||
|
return { x: Math.max( 0, Math.min( W - 1, px + d.x * n ) ), y: ty };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Objetivo de cada arquetipo. Las cuatro ramas comparten el filtro de
|
||||||
|
// candidatos y el desempate; solo cambia la celda contra la que se compara.
|
||||||
|
function ghostTarget( game, g ) {
|
||||||
|
const p = game.pacman;
|
||||||
|
const px = Math.round( p.x );
|
||||||
|
const py = Math.round( p.y );
|
||||||
|
|
||||||
|
// Blinky: perseguir sin mas.
|
||||||
|
if ( g.kind === 'blinky' ) return { x: px, y: py };
|
||||||
|
|
||||||
|
// Pinky: emboscarse 4 casillas por delante.
|
||||||
|
if ( g.kind === 'pinky' ) return aheadOfPacman( game, 4 );
|
||||||
|
|
||||||
|
// Inky: flanquear, reflejando a Blinky sobre el pivot a 2 casillas delante.
|
||||||
|
if ( g.kind === 'inky' ) {
|
||||||
|
const pivot = aheadOfPacman( game, 2 );
|
||||||
|
const blinky = game.ghosts.find( ( o ) => o.kind === 'blinky' );
|
||||||
|
// Sin Blinky (defensivo, no ocurre hoy) Pinky hace de suplente.
|
||||||
|
if ( !blinky ) return aheadOfPacman( game, 4 );
|
||||||
|
return {
|
||||||
|
x: 2 * pivot.x - Math.round( blinky.x ),
|
||||||
|
y: 2 * pivot.y - Math.round( blinky.y ),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clyde: persigue mientras este lejos y se retira a su esquina si se acerca.
|
||||||
|
if ( Math.abs( g.x - px ) + Math.abs( g.y - py ) > 8 ) return { x: px, y: py };
|
||||||
|
return { x: 0, y: 30 };
|
||||||
|
}
|
||||||
|
|
||||||
function decideGhost( game, g ) {
|
function decideGhost( game, g ) {
|
||||||
const grid = game.grid;
|
const grid = game.grid;
|
||||||
const p = game.pacman;
|
|
||||||
|
|
||||||
const options = Object.keys( DIRS ).filter(
|
const options = Object.keys( DIRS ).filter(
|
||||||
( dir ) => dir !== OPPOSITE[ g.dir ] && canMove( grid, g.x, g.y, dir, 'ghost' )
|
( dir ) => dir !== OPPOSITE[ g.dir ] && canMove( grid, g.x, g.y, dir, 'ghost' )
|
||||||
@@ -120,25 +163,21 @@ function decideGhost( game, g ) {
|
|||||||
// Sin salida (callejon): permitir el giro de 180.
|
// Sin salida (callejon): permitir el giro de 180.
|
||||||
const choices = options.length ? options : [ '' + OPPOSITE[ g.dir ] ];
|
const choices = options.length ? options : [ '' + OPPOSITE[ g.dir ] ];
|
||||||
|
|
||||||
if ( g.kind === 'hunter' ) {
|
const target = ghostTarget( game, g );
|
||||||
const px = Math.round( p.x );
|
|
||||||
const py = Math.round( p.y );
|
|
||||||
let best = choices[ 0 ];
|
let best = choices[ 0 ];
|
||||||
let bestDist = Infinity;
|
let bestDist = Infinity;
|
||||||
for ( const dir of choices ) {
|
for ( const dir of choices ) {
|
||||||
const d = DIRS[ dir ];
|
const d = DIRS[ dir ];
|
||||||
const nx = g.x + d.x;
|
const nx = g.x + d.x;
|
||||||
const ny = g.y + d.y;
|
const ny = g.y + d.y;
|
||||||
const dist = Math.abs( nx - px ) + Math.abs( ny - py );
|
const dist = Math.abs( nx - target.x ) + Math.abs( ny - target.y );
|
||||||
if ( dist < bestDist ) {
|
if ( dist < bestDist ) {
|
||||||
bestDist = dist;
|
bestDist = dist;
|
||||||
best = dir;
|
best = dir;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g.dir = best;
|
g.dir = best;
|
||||||
} else {
|
|
||||||
g.dir = choices[ Math.floor( Math.random() * choices.length ) ];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveGhost( game, g ) {
|
function moveGhost( game, g ) {
|
||||||
|
|||||||
+7
-2
@@ -51,9 +51,14 @@ const MAZE = MAZE_STR.map( ( row ) => row.split( '' ).map( parseTile ) );
|
|||||||
|
|
||||||
const TUNNEL_ROW = 14;
|
const TUNNEL_ROW = 14;
|
||||||
const PACMAN_START = { x: 13, y: 23 };
|
const PACMAN_START = { x: 13, y: 23 };
|
||||||
|
// Los cuatro arquetipos clasicos, dentro de la pen. Simetricos respecto al
|
||||||
|
// eje vertical (cols 11<->16, 13<->14) y en el orden de GHOST_COLORS de
|
||||||
|
// render.js (blinky rojo, pinky cyan, inky rosa, clyde naranja).
|
||||||
const GHOST_STARTS = [
|
const GHOST_STARTS = [
|
||||||
{ x: 13, y: 14, kind: 'hunter' }, // dentro de la pen
|
{ x: 11, y: 14, kind: 'blinky' },
|
||||||
{ x: 14, y: 14, kind: 'random' }, // dentro de la pen
|
{ x: 13, y: 14, kind: 'pinky' },
|
||||||
|
{ x: 14, y: 14, kind: 'inky' },
|
||||||
|
{ x: 16, y: 14, kind: 'clyde' },
|
||||||
];
|
];
|
||||||
|
|
||||||
window.MAZE = MAZE;
|
window.MAZE = MAZE;
|
||||||
|
|||||||
Reference in New Issue
Block a user