Last active
February 4, 2024 09:28
-
-
Save Muratam/42b24e4646e694fcbaa413a1c1ca9a0f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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