import { getColor , isEmpty , isInside , isInsideLarge , setColorEmpty , } from "@snk/types/grid" ; import { getHeadX , getHeadY , getSnakeLength , nextSnake , snakeEquals , snakeWillSelfCollide , } from "@snk/types/snake" ; import { around4 , Point } from "@snk/types/point" ; import { getBestTunnel } from "./getBestTunnel" ; import { fillOutside } from "./outside" ; import type { Outside } from "./outside" ; import type { Snake } from "@snk/types/snake" ; import type { Color , Empty , Grid } from "@snk/types/grid" ; export const clearCleanColoredLayer = ( grid , outside , snake0 , color ) => { const snakeN = getSnakeLength ( snake0 ) ; const points = getTunnellablePoints ( grid , outside , snakeN , color ) ; const chain = [ snake0 ] ; while ( points . length ) { const path = getPathToNextPoint ( grid , chain [ 0 ] , color , points ) ! ; path . pop ( ) ; for ( const snake of path ) setEmptySafe ( grid , getHeadX ( snake ) , getHeadY ( snake ) ) ; chain . unshift ( ... path ) ; } fillOutside ( outside , grid ) ; chain . pop ( ) ; return chain ; } ; type M = { snake : Snake ; parent : M | null ; } ; const unwrap = ( m ) : Snake [ ] => ! m ? [ ] : [ m . snake , ... unwrap ( m . parent ) ] ; const getPathToNextPoint = ( grid , snake0 , color , points ) => { const closeList = [ ] ; const openList = [ { snake : snake0 } as any ] ; while ( openList . length ) { const o = openList . shift ( ) ! ; const x = getHeadX ( o . snake ) ; const y = getHeadY ( o . snake ) ; const i = points . findIndex ( ( p ) => p . x === x && p . y === y ) ; if ( i >= 0 ) { points . splice ( i , 1 ) ; return unwrap ( o ) ; } for ( const { x : dx , y : dy } of around4 ) { if ( isInsideLarge ( grid , 2 , x + dx , y + dy ) && ! snakeWillSelfCollide ( o . snake , dx , dy ) && getColorSafe ( grid , x + dx , y + dy ) <= color ) { const snake = nextSnake ( o . snake , dx , dy ) ; if ( ! closeList . some ( ( s0 ) => snakeEquals ( s0 , snake ) ) ) { closeList . push ( snake ) ; openList . push ( { snake , parent : o } ) ; } } } } } ; /**
 * get all cells that are tunnellable
 */ export const getTunnellablePoints = ( grid , outside , snakeN , color ) => { const points = [ ] ; for ( let x = grid . width ; x -- ; ) for ( let y = grid . height ; y -- ; ) { const c = getColor ( grid , x , y ) ; if ( ! isEmpty ( c ) && c <= color && ! points . some ( ( p ) => p . x === x && p . y === y ) ) { const tunnel = getBestTunnel ( grid , outside , x , y , color , snakeN ) ; if ( tunnel ) for ( const p of tunnel ) if ( ! isEmptySafe ( grid , p . x , p . y ) ) points . push ( p ) ; } } return points ; } ; const getColorSafe = ( grid , x , y ) => isInside ( grid , x , y ) ? getColor ( grid , x , y ) : ( 0 as Empty ) ; const setEmptySafe = ( grid , x , y ) => { if ( isInside ( grid , x , y ) ) setColorEmpty ( grid , x , y ) ; } ; const isEmptySafe = ( grid , x , y ) => ! isInside ( grid , x , y ) && isEmpty ( getColor ( grid , x , y ) ) ;