222 lines
5.6 KiB
TypeScript
222 lines
5.6 KiB
TypeScript
import { Orientation } from "@prisma/client"
|
|
import { count } from "~/components/Gamefield/Gamefield"
|
|
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,
|
|
}
|
|
}
|