leaky-ships/leaky-ships/lib/utils/helpers.ts
2023-07-11 19:25:44 +02:00

222 lines
5.6 KiB
TypeScript

import { count } from "@components/Gamefield/Gamefield"
import { Orientation } from "@prisma/client"
import type {
Hit,
IndexedPosition,
Mode,
PointerProps,
Position,
ShipProps,
Target,
TargetList,
} from "../../interfaces/frontend"
export function borderCN(count: number, x: number, y: number) {
if (x === 0) return "left"
if (y === 0) return "top"
if (x === count + 1) return "right"
if (y === count + 1) return "bottom"
return ""
}
export function cornerCN(count: number, x: number, y: number) {
if (x === 0 && y === 0) return "left-top-corner"
if (x === count + 1 && y === 0) return "right-top-corner"
if (x === 0 && y === count + 1) return "left-bottom-corner"
if (x === count + 1 && y === count + 1) return "right-bottom-corner"
return ""
}
export function fieldIndex(count: number, x: number, y: number) {
return y * (count + 2) + x
}
export const modes: Mode[] = [
{
pointerGrid: Array.from(Array(1), () => Array.from(Array(1))),
type: "missile",
},
{
pointerGrid: Array.from(Array(3), () => Array.from(Array(1))),
type: "htorpedo",
},
{
pointerGrid: Array.from(Array(1), () => Array.from(Array(3))),
type: "vtorpedo",
},
{
pointerGrid: Array.from(Array(3), () => Array.from(Array(3))),
type: "radar",
},
]
function isBorder(x: number, y: number) {
return x < 2 || x > count + 1 || y < 2 || y > count + 1
}
export function isAlreadyHit(x: number, y: number, hits: Hit[]) {
return !!hits.filter((h) => h.x === x && h.y === y).length
}
export function targetList(
{ x: targetX, y: targetY }: Position,
modeInput: number | string,
): TargetList[] {
const mode =
typeof modeInput === "number"
? modeInput
: modes.findIndex((e) => e.type === modeInput)
if (mode < 0) return []
const { pointerGrid, type } = modes[mode]
const xLength = pointerGrid.length
const yLength = pointerGrid[0].length
return pointerGrid
.map((arr, i) => {
return arr.map((_, i2) => {
const relativeX = -Math.floor(xLength / 2) + i
const relativeY = -Math.floor(yLength / 2) + i2
const x = targetX + (relativeX ?? 0)
const y = targetY + (relativeY ?? 0)
return {
x,
y,
type,
edges: [
i === 0 ? "left" : "",
i === xLength - 1 ? "right" : "",
i2 === 0 ? "top" : "",
i2 === yLength - 1 ? "bottom" : "",
],
}
})
})
.reduce((prev, curr) => [...prev, ...curr], [])
}
export function overlapsWithAnyBorder(target: Position, mode: number) {
return !!targetList(target, mode).filter(({ x, y }) => isBorder(x, y)).length
}
export function composeTargetTiles(
target: Target,
mode: number,
hits: Hit[],
): PointerProps[] {
const { show } = target
return targetList(target, mode).map((targetItem) => {
const { x, y } = targetItem
return {
...targetItem,
show,
imply: isAlreadyHit(x, y, hits),
}
})
}
export const initlialTarget = {
x: 2,
y: 2,
show: false,
orientation: Orientation.h,
}
export const initlialTargetPreview = {
x: 2,
y: 2,
show: false,
orientation: Orientation.h,
}
export const initlialMouseCursor = {
shouldShow: false,
x: 0,
y: 0,
}
export const shipProps = (
ships: ShipProps[],
mode: number,
targetPreview: Position & { orientation: Orientation },
) => ({
size: mode + 2,
variant:
ships
.filter((e) => e.size === mode + 2)
.sort((a, b) => a.variant - b.variant)
.reduce((prev, curr) => {
return curr.variant - prev < 2 ? curr.variant : prev
}, 0) + 1,
x:
targetPreview.orientation === "h"
? targetPreview.x - Math.floor((mode + 2) / 2)
: targetPreview.x,
y:
targetPreview.orientation === "v"
? targetPreview.y - Math.floor((mode + 2) / 2)
: targetPreview.y,
orientation: targetPreview.orientation,
})
export function shipFields(ship: ShipProps, i?: number) {
let fields: IndexedPosition[] = []
let borders: IndexedPosition[] = []
for (
let x = ship.x;
x <= (ship.orientation === "h" ? ship.x + ship.size - 1 : ship.x);
x++
) {
for (
let y = ship.y;
y <= (ship.orientation === "v" ? ship.y + ship.size - 1 : ship.y);
y++
) {
fields.push({ x, y, i })
}
}
for (
let x = ship.x - 1;
x <= (ship.orientation === "h" ? ship.x + ship.size : ship.x + 1);
x++
) {
for (
let y = ship.y - 1;
y <= (ship.orientation === "v" ? ship.y + ship.size : ship.y + 1);
y++
) {
if (isBorder(x, y) || fields.filter((e) => e.x === x && e.y === y).length)
continue
borders.push({ x, y, i })
}
}
return { fields, borders }
}
export function intersectingShip(
ships: ShipProps[],
ship: ShipProps,
withBorder?: boolean,
) {
const thisShip = shipFields(ship)
const reducedShips = ships.reduce(
(prev, curr, i) => {
const { fields, borders } = shipFields(curr, i)
return {
fields: [...prev.fields, ...fields],
borders: [...prev.borders, ...borders],
}
},
{ fields: [] as IndexedPosition[], borders: [] as IndexedPosition[] },
)
const fields = reducedShips.fields.filter(
(e) => thisShip.fields.filter((e2) => e2.x === e.x && e2.y === e.y).length,
)
const borders = thisShip.fields.filter(
(e) =>
reducedShips.borders.filter((e2) => e2.x === e.x && e2.y === e.y).length,
)
const isInBorder = thisShip.fields.filter(
(e) => withBorder && isBorder(e.x, e.y),
)
return {
score: isInBorder.length ? 2 : fields.length || borders.length ? 1 : 0,
index: fields[0]?.i,
fields,
borders,
}
}