Files
Sebastian Seedorf fadfa67574 Fixed name matching
2022-09-11 14:10:49 +02:00

195 lines
7.4 KiB
TypeScript

import { useRouter } from 'next/router'
import { useGroups } from '../contexts/GroupProvider'
import { FC, useMemo, useState } from 'react'
import { calculateInputs } from '../../src/calculateInputs'
import { DetailGraphNode, NodeDetails } from './NodeDetails/NodeDetails'
import { groupBy, isNonNullable, uniquify } from '../../src/utils'
import { EnrichedEntity } from '../../src/types'
import Head from 'next/head'
import { ScrollContainer } from './ScrollContainer/ScrollContainer'
import { ProducingGraph } from './ProducingGraph/ProducingGraph'
import { useFactories } from '../contexts/FactoryProvider'
import { i18n, I18n } from '../shared/I18n/I18n'
import { useIntl } from 'react-intl'
import { Button } from '../shared/Button/Button'
import { useAsyncEffect } from '../../src/hooks/useAsyncEffect'
export const PageDetails: FC = () => {
const intl = useIntl()
const {
query: { name }
} = useRouter()
const { exportedFactories, baseFactories, groups } = useGroups()
const { findFactory } = useFactories()
const group = typeof name === 'string' ? groups[name] : undefined
const [inputFactories, intermediateFactories] = useMemo<
ReturnType<typeof calculateInputs>
>(() => {
if (!group) return [[], []]
return calculateInputs(
[...group.exports, ...group.malls],
baseFactories,
exportedFactories,
findFactory
)
}, [baseFactories, exportedFactories, findFactory, group])
const producingNodes: DetailGraphNode[] = useMemo(() => {
if (!group) return []
const nodes = uniquify([...intermediateFactories, ...group.exports, ...group.malls])
.map(findFactory)
.filter(isNonNullable)
.map(
(factory: EnrichedEntity) =>
({
inputs: Object.keys(factory.recipe?.prerequisites ?? {}).sort((a, b) =>
a.localeCompare(b)
),
outputs: Object.keys(factory.recipe?.output ?? {}).sort((a, b) => a.localeCompare(b)),
name: factory.name,
recipes: [factory.recipe]
} as DetailGraphNode)
)
return Object.values(groupBy(nodes, node => node.inputs.join())).map(
nodesOfInput =>
({
inputs: nodesOfInput[0].inputs,
outputs: uniquify(nodesOfInput.flatMap(node => node.outputs)),
name: nodesOfInput.map(node => node.name).join(', '),
recipes: nodesOfInput.flatMap(node => node.recipes)
} as DetailGraphNode)
)
}, [findFactory, group, intermediateFactories])
const [savingModuleText, setSavingModuleText] = useState<string | undefined>()
// eslint-disable-next-line react-hooks/exhaustive-deps
useAsyncEffect(async () => {
const res = await fetch('/factorio/modules/energy-saving.csv')
if (res.ok) setSavingModuleText(await res.text())
}, [])
const [optimalModuleText, setOptimalModuleText] = useState<string | undefined>()
// eslint-disable-next-line react-hooks/exhaustive-deps
useAsyncEffect(async () => {
const res = await fetch('/factorio/modules/few-beacons.csv')
if (res.ok) setOptimalModuleText(await res.text())
}, [])
const statisticsUrl = useMemo(() => {
const toDashedKey = (n: string) => {
const key = n.slice(1).toLowerCase().replace(/_/g, '-').replace('efficiency', 'effectivity')
return (
{
wall: 'stone-wall',
lamp: 'small-lamp',
'active-provider-chest': 'logistic-chest-active-provider',
'passive-provider-chest': 'logistic-chest-passive-provider',
'storage-chest': 'logistic-chest-storage',
'buffer-chest': 'logistic-chest-buffer',
'requester-chest': 'logistic-chest-requester',
'uranium-235': 'uranium-ore',
'uranium-238': 'uranium-ore',
exoskeleton: 'exoskeleton-equipment',
'portable-fusion-reactor': 'fusion-reactor-equipment'
}[key] ?? key
)
}
const checkIfPresent = (dashedKey: string) => {
const b = !!optimalModuleText?.match(new RegExp(`\\n${dashedKey};`))
if (optimalModuleText && !b) console.warn('Not found element', dashedKey)
return b
}
const getModuleString = (dashedKey: string, powerSaving?: boolean) => {
const match = (powerSaving ? savingModuleText : optimalModuleText)?.match(
new RegExp(`${dashedKey};.*;(\\d+);(\\d+);(\\d+);(\\d+)`)
)
if (match) {
const map = 'spe'
const [beacons, ...modules] = match.slice(1).map(x => +x)
let beaconModules = beacons == 1 ? 8 : beacons == 5 ? 12 : 0
const maxIdx = modules.reduce((m, c, i, arr) => (c > arr[m] ? i : m), 0)
modules[maxIdx] -= beaconModules
if (modules[maxIdx] < 0) {
const newMaxIdx = modules.reduce((m, c, i, arr) => (c > arr[m] ? i : m), 0)
modules[newMaxIdx] += modules[maxIdx]
beaconModules += modules[maxIdx]
modules[maxIdx] = 0
}
let res = [
dashedKey,
...[0, 1, 2].flatMap(idx => Array(modules[idx]).fill(`${map[idx]}3`))
].join(':')
if (beacons + modules[0] + modules[1] === 0 && modules[2] === 2)
res = res.replace(/e3/g, 'e2')
else if (beacons + modules[0] + modules[1] === 0 && modules[2] >= 3)
res = res.replace(/e3/g, 'ee')
// console.log(beacons, modules, res, dashedKey)
return res + (beaconModules ? `;${map[maxIdx]}3:${beaconModules}` : '')
}
return dashedKey
}
const params = {
data: '1-1-19',
rate: 's',
cp: '2',
min: '3',
belt: 'express-transport-belt',
items: [
...(group?.malls
.map(toDashedKey)
.filter(checkIfPresent)
.map(n => `${n}:r:1`) ?? []),
...(group?.exports
.map(toDashedKey)
.filter(checkIfPresent)
.map(n => `${n}:r:45`) ?? [])
].join(','),
ignore: inputFactories.map(toDashedKey).filter(checkIfPresent).join(','),
modules: [
...([...(group?.exports ?? []), ...intermediateFactories, ...inputFactories]
.map(toDashedKey)
.filter(checkIfPresent)
.map(n => getModuleString(n)) ?? []),
...((group?.malls ?? [])
.map(toDashedKey)
.filter(checkIfPresent)
.map(n => getModuleString(n, true)) ?? [])
].join(',')
}
const paramString = Object.entries(params)
.map(keyValue => keyValue.join('='))
.join('&')
// console.log('params', 'https://kirkmcdonald.github.io/calc.html#' + paramString)
return 'https://kirkmcdonald.github.io/calc.html#' + paramString
}, [group, inputFactories, savingModuleText, optimalModuleText, intermediateFactories])
return (
<>
<Head>
<title>
<I18n id={'page.visualize.details.title'} values={{ name: group?.name ?? '' }} />
</title>
<meta name='description' content={i18n(intl, 'page.home.head.meta.description')} />
</Head>
<main>
<ScrollContainer>
<h1>
<I18n id={'page.visualize.details.title'} values={{ name: group?.name ?? '' }} />
</h1>
<Button onClick={() => window.open(statisticsUrl, '_blank')}>
<I18n id={'page.visualize.details.statistics.button'} />
</Button>
<ProducingGraph
nodes={producingNodes}
inputs={inputFactories}
outputs={group?.exports}
childType={NodeDetails}
/>
</ScrollContainer>
</main>
</>
)
}