Skip to content

Instantly share code, notes, and snippets.

@Muratam
Last active February 4, 2024 09:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Muratam/42b24e4646e694fcbaa413a1c1ca9a0f to your computer and use it in GitHub Desktop.
Save Muratam/42b24e4646e694fcbaa413a1c1ca9a0f to your computer and use it in GitHub Desktop.
// ts-node nerke.ts
const alchesRaw = [
["シングルスカット", "武器", 1077, 13, 23, { 木弾の実: 1, フラム: 1, 玉鋼石: 3, アイヒェ: 2 }],
["開拓便利ツール", "雑貨", 1888, 18, 21, { 磨いた水晶: 1, シルヴァリア: 1, ラバーツリー: 1, 土: 5 }],
["クリスタファング", "武器", 1144, 14, 21, { 連岩: 1, 磨いた水晶: 1, ペンデローク: 1, 水: 2 }],
["音手箱", "雑貨", 1086, 13, 21, { ひかる円盤: 1, くず鉄: 1, ピュアオイル: 1 }],
["アーマードバンカー", "武器", 1048, 13, 21, { インゴット: 1, くず鉄: 1, 連岩: 1, 火: 2 }],
["ベジまんじゅう", "食材", 698, 10, 21, { 小麦粉: 3, 葉レタス: 3, 何かのタマゴ: 5, 風: 5 }],
["フリューゲル", "服飾", 524, 8, 21, { 動物の皮革: 1, 水: 10, 風: 5 }],
["干し草用フォーク", "建材", 0, 7, 21, { ラバーツリー: 1, キンバー鉱石: 1, 風: 5 }],
["放電管", "雑貨", 996, 13, 19, { 永久結晶: 1, ジャンク品: 1, くず鉄: 2 }],
["クラフト", "武器", 962, 11, 19, { うに: 2, インゴット: 1, "中和剤・赤": 1, 火: 5 }],
["バトルメイル", "武器", 930, 12, 19, { モフコット: 1, 鎖グモの巣: 2, 水: 3, 土: 3 }],
["乙女の槍", "武器", 906, 11, 19, { インゴット: 1, ティンバー: 1, うに: 3, 火: 5 }],
["錬金ドロップ", "食材", 760, 10, 19, { きまぐれいちご: 2, ハチの巣: 1, ゼッテル: 1, にんじん: 1 }],
["モフコット", "服飾", 401, 7, 19, { 羊毛: 2, 水: 10 }],
["ケーキ屋", "建物", 0, 0, -1, { いびつな石材: 5, 樹氷石: 10, 火: 30 }],
["調合機材", "建材", 0, 14, -1, { インゴット: 1, 磨いた水晶: 1, ジャンク品: 1 }],
["大きなアトリエ", "建物", 0, 0, -1, { 調合機材: 1, いびつな石材: 3, ティンバー: 3 }],
["金剛石", "武器", 765, 12, 15, { キンバー鉱石: 1, 錬金粘土: 1, 玉鋼石: 1 }],
["目覚めの懐中時計", "雑貨", 453, 11, 15, { ひかる円盤: 1, ジャンク品: 1, 鎖グモの巣: 2, 土: 3 }],
["シルヴァリア", "武器", 425, 11, 15, { 鋼鉄鉱: 2, 火: 10 }],
["ハチミツ", "食材", 238, 8, 15, { ハチの巣: 3, 年代物のつぼ: 1, 火: 5 }],
["超硬合金", "建材", 0, 16, 15, { 連岩: 1, 金剛石: 1, 火: 5 }],
["錬金炭", "雑貨", 504, 12, 13, { ラバーツリー: 2, フロジストン: 2, 火: 10 }],
["ドナーストーン", "武器", 415, 10, 13, { ライデン鉱: 1, 砕けた石材: 1, "中和剤・緑": 1, 土: 3 }],
["ネクタル", "薬", 327, 9, 13, { ペンデローク: 1, 陽性の日傘: 1, シャリオミルク: 1, 水: 3 }],
["水晶玉", "雑貨", 530, 11, 12, { 磨いた水晶: 1, 研磨剤: 3 }],
["ピュアオイル", "食材", 318, 8, 12, { 植物油: 2, タールの実: 1, "中和剤・青": 1, 火: 5 }],
["錬金粘土", "雑貨", 280, 8, 12, { ペンデローク: 1, ジャンク品: 1, 土: 3 }],
["中和剤・黄", "雑貨", 169, 6, 12, { ライデン鉱: 1, 土: 4 }],
["アイアンボード", "建材", 0, 9, 12, { キンバー鉱石: 1, 陽晶石: 1, 火: 5 }],
["フルーティー", "薬", 190, 6, 11, { 甘露の実: 2, 紫ぶどう: 1, 飲める水: 1, シロヒメクサ: 1 }],
["シャベル", "武器", 360, 6, 9, { アイヒェ: 1, インゴット: 1, 土: 3 }],
["清らかな香炉", "雑貨", 170, 6, 9, { 香木のくず: 2, アイヒェ: 1, 年代物のつぼ: 1, 土: 3 }],
["レヘルン", "武器", 350, 9, 8, { 樹氷石: 1, 飲める水: 1, "中和剤・青": 1 }],
["磨いた水晶", "雑貨", 403, 9, 8, { 永久結晶: 1, クロース: 1 }],
["ゼッテル", "雑貨", 287, 8, 8, { トーン: 3, ハチの巣: 3, 風: 5 }],
["リンゴのタルト", "食材", 266, 7, 8, { 小麦粉: 3, リンゴ: 2, 紫ぶどう: 2, 水: 3 }],
["クロース", "服飾", 209, 7, 8, { 鎖グモの巣: 2, 土: 3 }],
["研磨剤", "雑貨", 97, 4, 8, { フェスト: 1, 土: 1 }],
["中和剤・青", "雑貨", 96, 4, 8, { タールの実: 1, 水: 4 }],
["マジカルペイント", "雑貨", 275, 6, 7, { ぷにぷに玉: 1, "中和剤・赤": 1, 植物油: 1, 土: 2 }],
["インゴット", "武器", 234, 6, 7, { 玉鋼石: 2, 火: 5 }],
["祝福のワイン", "食材", 219, 5, 7, { 紫ぶどう: 3, 蒸留水: 1, 水: 2 }],
["プニゼリー", "食材", 201, 6, 7, { ぷにぷに玉: 1, 飲める水: 1, リンゴ: 1, 火: 1 }],
["ヒーリングサルブ", "薬", 154, 4, 7, { トーン: 1, 植物油: 1, 飲める水: 1 }],
["癒やしのアロマ", "雑貨", 140, 4, 7, { 香木のくず: 1, リンゴ: 1, 風: 3, 火: 3 }],
["虫取り網", "雑貨", 94, 3, 7, { 絹グモの巣: 1, アイヒェ: 1 }],
["蒸留水", "食材", 92, 3, 7, { 香木のくず: 2, 水: 4 }],
["中和剤・緑", "雑貨", 88, 3, 7, { トーン: 1, 風: 4 }],
["雑貨屋", "建物", 0, 0, 7, { ティンバー: 2 }],
["フラム", "武器", 253, 7, 6, { フロジストン: 1, タールの実: 1, "中和剤・赤": 1 }],
["素朴な焼き菓子", "食材", 112, 4, 6, { 小麦粉: 2, うに: 2 }],
["中和剤・赤", "雑貨", 87, 4, 6, { 植物油: 1, 火: 4 }],
["伝統食パン", "食材", 135, 4, 4, { 小麦粉: 1, 植物油: 1, 火: 3 }],
["ビア", "食材", 59, 2, 4, { 小麦: 2, 香木のくず: 1, 水: 1 }],
["小麦粉", "食材", 35, 1, 4, { 小麦: 2 }],
["いびつな石材", "建材", 0, 7, 4, { 砕けた石材: 1, フェスト: 1, 土: 3 }],
["ティンバー", "建材", 0, 2, 4, { アイヒェ: 2, タールの実: 1 }],
];
const agrisRaw = [
["畑", [
["小麦", 16, {}],
["植物油", 14, { 風: 10 }],
["紫ぶどう", 14, { 水: 5, 風: 5 }],
["甘露の実", 12, { 水: 10 }],
["きまぐれいちご", 10, {}],
["にんじん", 10, {}],
["葉レタス", 8, {}],
["マンドラゴラの根", 8, {}],
["赤い悪魔", 2, {}],
]],
["林", [
["うに", 16, {}],
["タールの実", 16, { 火: 10 }],
["アイヒェ", 16, {}],
["香木のくず", 16, {}],
["リンゴ", 14, { 水: 10 }],
["ハチの巣", 12, {}],
["ラバーツリー", 10, {}],
["妖精の日傘", 10, { 土: 35 }],
["黄金樹の葉", 8, {}],
["ヤミヨタケ", 6, {}],
]],
["花畑", [
["トーン", 14, { 風: 10 }],
["シロヒメクサ", 10, {}],
["ハニーポピー", 8, {}],
["忘却の傷無し草", 4, {}],
]],
["牧場", [
["シャリオミルク", 12, {}],
["羊毛", 12, {}],
["丈夫な骨", 6, {}],
["何かのタマゴ", 6, {}],
["動物の皮革", 6, {}],
["邪悪なる牙", 4, {}],
]]
]
class Alchemy {
// 価格, コスト, 取得ターン, 必要な素材
constructor(name: string, saleType: string, price: number, cost: number, turn: number, requires: any) {
this.name = name;
this.saleType = saleType;
this.price = price;
this.cost = cost;
this.turn = turn;
this.requires = new Map<string, number>();
for (let key in requires) {
this.requires.set(key, requires[key])
}
}
name: string;
saleType: string;
price: number;
cost: number;
turn: number;
requires: Map<string, number>;
}
class Agri {
// 栽培効率
constructor(space: string, name: string, count: number, primProduces: any) {
this.space = space;
this.name = name;
this.count = count;
this.primProduces = new Map<string, number>();
for (let key in primProduces) {
this.primProduces.set(key, primProduces[key])
}
}
space: string;
name: string;
count: number;
primProduces: Map<string, number>;
}
class Requirement {
totalAlchemyCost = 0;
alchemies = new Map<string, number>();
agris = new Map<string, number>();
researches = new Map<string, number>();
prims = new Map<string, number>();
}
class Solver {
constructor(turn: number) {
const alches = alchesRaw.map(x => new Alchemy(x[0] as string, x[1] as string, x[2] as number, x[3] as number, x[4] as number, x[5] as any)) as Alchemy[]
const agris = agrisRaw.map(x => (x[1] as any).map((y: any) => new Agri(x[0] as string, y[0] as string, y[1] as number, y[2]))).flat() as Agri[]
const primList = ["火", "水", "土", "風"]
class PrimGenerateState {
agriName = ""
totalPrimCount = 0
}
let alcheTable = new Map<string, Alchemy>()
let agriTable = new Map<string, Agri>()
let bestPrimTable = new Map<string, PrimGenerateState>()
for (const alche of alches.filter(x => x.turn <= turn)) alcheTable.set(alche.name, alche)
for (const agri of agris) {
agriTable.set(agri.name, agri)
// bestPrim
let currentState = new PrimGenerateState()
currentState.agriName = agri.name
for (let [_, count] of agri.primProduces.entries()) {
currentState.totalPrimCount += count * agri.count
}
for (let [primName, _] of agri.primProduces.entries()) {
let bestPrim = bestPrimTable.get(primName)
if (bestPrim) {
if (bestPrim.totalPrimCount > currentState.totalPrimCount) continue
if (bestPrim.totalPrimCount == currentState.totalPrimCount) {
// 5+5 vs 10 問題があれば
}
}
bestPrimTable.set(primName, currentState)
}
}
const getBestPrimStr = (primName: string, count: number): string => {
let bestPrim = bestPrimTable.get(primName)
if (bestPrim) {
let agri = agriTable.get(bestPrim.agriName)
if (agri) {
let produceCount = agri.primProduces.get(primName) || 0
return `[${primName}:${agri.space}x${(count / produceCount).toFixed(2)}/${agri.count}]${agri.name}, `
}
}
return "[還元]???, "
}
let calcTotalAgriSpaceCount = (require: Requirement): number => {
let totalAgriSpaceCount = 0;
for (const [k, v] of require.agris) {
let agri = agriTable.get(k);
totalAgriSpaceCount += v / (agri?.count || 1);
}
return totalAgriSpaceCount
}
let getStrNeedList = (require: Requirement): string => {
let result = ""
for (const [k, v] of require.alchemies) {
result += `[調合x${v}]${k}, `
}
for (const [k, v] of require.agris) {
let agri = agriTable.get(k);
result += `[${agri?.space}x${v}/${agri?.count}]${k}, `
}
for (const [k, v] of require.prims) {
result += getBestPrimStr(k, v)
}
for (const [k, v] of require.researches) {
result += `[調査x${v}]${k}, `
}
return result
}
let calcRequie = (target: Agri | Alchemy): Requirement => {
const add = (target: Map<string, number>, key: string, count: number) => {
target.set(key, (target.get(key) || 0) + count)
}
const merge = (target: Map<string, number>, src: Map<string, number>) => {
for (const [k, v] of src) add(target, k, v)
}
let result = new Requirement()
if (target instanceof Agri) {
add(result.agris, target.name, 1)
} else {
add(result.alchemies, target.name, 1)
result.totalAlchemyCost += target.cost
for (const [requireName, requireCount] of target.requires) {
for (let _ = 0; _ < requireCount; _++) {
const alcheOrAgri = alcheTable.get(requireName) || agriTable.get(requireName)
if (alcheOrAgri) {
const require = calcRequie(alcheOrAgri)
result.totalAlchemyCost += require.totalAlchemyCost
merge(result.alchemies, require.alchemies)
merge(result.agris, require.agris)
merge(result.prims, require.prims)
merge(result.researches, require.researches)
} else if (primList.includes(requireName)) {
add(result.prims, requireName, 1)
} else {
add(result.researches, requireName, 1)
}
}
}
}
return result
}
this.calcRequie = calcRequie
this.getStrNeedList = getStrNeedList
this.calcTotalAgriSpaceCount = calcTotalAgriSpaceCount
this.alcheTable = alcheTable
}
alcheTable: Map<string, Alchemy>;
calcRequie: (target: Agri | Alchemy) => Requirement;
getStrNeedList: (require: Requirement) => string;
calcTotalAgriSpaceCount: (require: Requirement) => number;
}
// 指定したターンまでの内容で効率を標準出力
enum ResearchQuery { OnlyNoResearch = 0, OnlyResearch = 1, Both = 2, }
let saleTypeIconTable = { 建材: "■", 薬: "💊", 雑貨: "🧸", 食材: "🍚", 武器: "🗡", 建物: "🏠", 服飾: "👕" } as any
function outputTurnInfo(turn: number, researchQuery: ResearchQuery) {
let solver = new Solver(turn)
// スケールするためには栽培できる必要
let requires: any[] = []
for (const [targetName, target] of solver.alcheTable) {
const require = solver.calcRequie(target);
requires.push([targetName, target, require])
}
requires.sort((x, y) => x[1].price - y[1].price)
for (let [targetName, target, require] of requires) {
if (researchQuery === ResearchQuery.OnlyNoResearch) {
if (require.researches.size > 0) continue
} else if (researchQuery === ResearchQuery.OnlyResearch) {
if (require.researches.size === 0) continue
} else if (researchQuery === ResearchQuery.Both) {
}
let totalAgriSpaceCount = solver.calcTotalAgriSpaceCount(require)
console.log(
`${saleTypeIconTable[target.saleType]} ${targetName} [${target.price},${(target.price / require.totalAlchemyCost).toFixed(0)},${(target.price / totalAgriSpaceCount).toFixed(0)}] `
+ `:: ${solver.getStrNeedList(require)}`)
}
}
outputTurnInfo(100, ResearchQuery.OnlyNoResearch)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment