'use strict'
class Cartographer {
constructor () {}
start () {
const nodeData = new Map()
this.propagateFrom(nodeData, "fa")
this.matchEdges(nodeData)
const dataByDepth = Array.from(nodeData.values())
dataByDepth.sort((p, q) => {
if (p.depth != q.depth) {
return p.depth - q.depth
}
return p.from - q.from
})
nodeData.get("fa").solved = true
nodeData.get("ea").position.x = 10
nodeData.get("ea").solved = true // Entente entrance
nodeData.get("eq").position.y = 10
nodeData.get("eq").solved = true // Entente exit
const unsolved = this.solve(nodeData, dataByDepth)
for (const datum of unsolved) {
console.log('Unsolved:', datum)
}
const colors = new Map([
[Zone.forest, '#FF0000'],
[Zone.studio, '#FF8000'],
[Zone.circular, '#FFFF00'],
[Zone.stones, '#00FF00'],
[Zone.rainre, '#00FFFF'],
[Zone.antechannel, '#0000FF'],
[Zone.metamondst, '#FF00FF'],
[Zone.capsule, '#808080'],
[Zone.nataniev, '#C0C0C0'],
[Zone.entente, '#FFFFFF']
])
const nodeOutput = []
const edgeOutput = []
for (const datum of nodeData.values()) {
nodeOutput.push(
`${
datum.nodeID
}${
datum.nodeID
}${datum
.position.x * 60}${datum
.position.y *
60}30.030.0rectangle${colors.get(
datum.zone
)}`
)
for (const edge of datum.edges) {
edgeOutput.push(
`${
edge.from
}${
edge.to
}`
)
}
}
const outputPrefix = `1`
const outputSuffix = ``
const output =
outputPrefix +
nodeOutput.join('\n\t\t') +
'\n\n\t\t' +
edgeOutput.join('\n\t\t') +
outputSuffix
console.clear()
console.log(output)
}
matchEdges (nodeData) {
let edgeID = 0
for (const datum of nodeData.values()) {
for (const edge of datum.edges) {
if (edge.id == null) {
edge.id = edgeID++
}
}
}
for (const datum of nodeData.values()) {
for (const edge of datum.edges) {
if (edge.match == null) {
const otherDatum = nodeData.get(edge.to)
for (const otherEdge of otherDatum.edges) {
if (otherEdge.match == null && otherEdge.to == datum.nodeID) {
edge.match = otherEdge.id
otherEdge.match = edge.id
}
}
}
}
}
}
solve (nodeData, dataByDepth) {
const unsolved = []
for (const datum of dataByDepth) {
datum.edges.sort(
function (p, q) {
const pZoneDiff = nodeData.get(p.to).zone == datum.zone ? 0 : 1
const qZoneDiff = nodeData.get(q.to).zone == datum.zone ? 0 : 1
if (pZoneDiff != qZoneDiff) {
return pZoneDiff - qZoneDiff
}
const pDiff = (p.toOrientation - p.fromOrientation + 8) % 4
const qDiff = (p.toOrientation - p.fromOrientation + 8) % 4
if (pDiff != qDiff) {
return pDiff - qDiff
}
if (p.priority != q.priority) {
return p.priority - q.priority
}
return nodeData.get(p.to).depth - nodeData.get(q.to).depth
}
)
const edges = datum.edges.filter(
function (edge) {
const toDatum = nodeData.get(edge.to)
return toDatum.solved && toDatum.depth < datum.depth
}
)
for (const edge of edges) {
if (nodeData.get(edge.to).depth < datum.depth) {
datum.solved = true
this.deriveNodeSpace(datum, nodeData.get(edge.to), edge)
break
}
}
if (!datum.solved) {
unsolved.push(datum)
}
}
return unsolved
}
deriveNodeSpace (datum, otherDatum, edge) {
datum.position.x = otherDatum.position.x
datum.position.y = otherDatum.position.y
const toOrientation = (edge.toOrientation + otherDatum.heading) % 4
const fromOrientation = edge.fromOrientation
datum.heading = (fromOrientation - toOrientation + 4) % 4
switch (toOrientation) {
case 0:
datum.position.y++
break
case 1:
datum.position.x--
break
case 2:
datum.position.y--
break
case 3:
datum.position.x++
break
}
}
propagateFrom (nodeData, nodeID, depth = 0) {
const recordEdge = (
from,
fromOrientation,
to,
toField,
toOrientationField,
priority,
puzzleID = null
) => {
return {
from,
fromOrientation,
to: to[toField],
toOrientation:
to[toOrientationField] == null
? fromOrientation
: to[toOrientationField],
priority,
puzzleID,
match: null
}
}
const node = nodesByID.get(nodeID)
if (nodeData.has(nodeID)) {
return
}
const datum = {
nodeID,
zone: node.zone,
depth,
solved: false,
position: { x: 0, y: 0 },
heading: 0,
edges: []
}
nodeData.set(nodeID, datum)
const neighborIDs = new Set()
for (let i = 0; i < 4; i++) {
const subject = node.subjects[i]
if (subject.mapIgnore) {
continue
}
if (subject.nodeID != null) {
datum.edges.push(
recordEdge(
nodeID,
i,
subject,
'nodeID',
'orientation',
subject.type == SubjectType.node ? 1 : 2,
subject.puzzleID
)
)
neighborIDs.add(subject.nodeID)
}
if (subject.alternateNodeID != null) {
datum.edges.push(
recordEdge(
nodeID,
i,
subject,
'alternateNodeID',
'alternateOrientation',
3,
subject.puzzleID
)
)
neighborIDs.add(subject.alternateNodeID)
}
}
for (const id of neighborIDs.keys()) {
this.propagateFrom(nodeData, id, depth + 1)
}
}
}