import { Empty , getColor , isEmpty , isInside , setColorEmpty , } from "@snk/types/grid" ; import { getHeadX , getHeadY , getSnakeLength } from "@snk/types/snake" ; import { getBestTunnel } from "./getBestTunnel" ; import { fillOutside , Outside } from "./outside" ; import { getTunnelPath } from "./tunnel" ; import { getPathTo } from "./getPathTo" ; import type { Snake } from "@snk/types/snake" ; import type { Color , Grid } from "@snk/types/grid" ; import type { Point } from "@snk/types/point" ; type T = Point & { tunnel : Point [ ] ; priority : number ; } ; export const clearResidualColoredLayer = ( grid , outside , snake0 , color ) => { const snakeN = getSnakeLength ( snake0 ) ; const tunnels = getTunnellablePoints ( grid , outside , snakeN , color ) ; tunnels . sort ( ( a , b ) => b . priority - a . priority ) ; const chain = [ snake0 ] ; while ( tunnels . length ) { let t = getNextTunnel ( tunnels , chain [ 0 ] ) ; chain . unshift ( ... getPathTo ( grid , chain [ 0 ] , t [ 0 ] . x , t [ 0 ] . y ) ! ) ; chain . unshift ( ... getTunnelPath ( chain [ 0 ] , t ) ) ; for ( const { x , y } of t ) setEmptySafe ( grid , x , y ) ; fillOutside ( outside , grid ) ; for ( let i = tunnels . length ; i -- ; ) if ( isEmpty ( getColor ( grid , tunnels [ i ] . x , tunnels [ i ] . y ) ) ) tunnels . splice ( i , 1 ) ; else { const t = tunnels [ i ] ; const tunnel = getBestTunnel ( grid , outside , t . x , t . y , color , snakeN ) ; if ( ! tunnel ) tunnels . splice ( i , 1 ) ; else { t . tunnel = tunnel ; t . priority = getPriority ( grid , color , tunnel ) ; } } tunnels . sort ( ( a , b ) => b . priority - a . priority ) ; } chain . pop ( ) ; return chain ; } ; const getNextTunnel = ( ts , snake ) => { let minDistance = Infinity ; let closestTunnel = null ; const x = getHeadX ( snake ) ; const y = getHeadY ( snake ) ; const priority = ts [ 0 ] . priority ; for ( let i = 0 ; ts [ i ] && ts [ i ] . priority === priority ; i ++ ) { const t = ts [ i ] . tunnel ; const d = distanceSq ( t [ 0 ] . x , t [ 0 ] . y , x , y ) ; if ( d < minDistance ) { minDistance = d ; closestTunnel = t ; } } return closestTunnel ! ; } ; /**
 * get all the tunnels for all the cells accessible
 */ 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 ) { const tunnel = getBestTunnel ( grid , outside , x , y , color , snakeN ) ; if ( tunnel ) { const priority = getPriority ( grid , color , tunnel ) ; points . push ( { x , y , priority , tunnel } ) ; } } } return points ; } ; /**
 * get the score of the tunnel
 * prioritize tunnel with maximum color smaller than <color> and with minimum <color>
 * with some tweaks
 */ export const getPriority = ( grid , color , tunnel ) => { let nColor = 0 ; let nLess = 0 ; for ( let i = 0 ; i < tunnel . length ; i ++ ) { const { x , y } = tunnel [ i ] ; const c = getColorSafe ( grid , x , y ) ; if ( ! isEmpty ( c ) && i === tunnel . findIndex ( ( p ) => p . x === x && p . y === y ) ) { if ( c === color ) nColor += 1 ; else nLess += color - c ; } } if ( nColor === 0 ) return 99999 ; return nLess / nColor ; } ; const distanceSq = ( ax , ay , bx , by ) => ( ax - bx ) ** 2 + ( ay - by ) ** 2 ; 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 ) ; } ;