请你解决使用任意格数的合成方块输入合成表时依然是9格输入的问题,并且在设置了输出槽后,不应该有输出数量询问了(直接在输出槽里回答),并且,统计材料和逐步合成都是看输入框里的物品的,合成出的是输出框里的物品,此外,我们发现程序在启动时并不能找到之前的保存位置,请用一个文件来设置保存位置,另外,缺少头文件<unordered_set>。
代码:
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <algorithm>
#include <cctype>
#include <filesystem>
#include <iomanip>
#include <limits>
#include <sstream>
#include <cmath>
#include <unordered_map>
#include <unordered_set>
#include <functional>
#include <memory>
#include <variant>
#include <queue>
#include <stack>
#include <array>
#include <thread>
#include <climits>
#include <cctype>
#include <type_traits>
#include <string_view>
using namespace std;
namespace fs = filesystem;
using namespace std::literals;
// ===================== 函数预定义 =====================
void clear(), wait(int),pause();
// ====================== 类型定义 ======================
using CraftGrid = array<array<pair<string, short>, 3>, 3>;
using MaterialMap = unordered_map<string, long long>;
// ====================== 常量定义 ======================
constexpr short DEFAULT_MAX_STACK = 64;
constexpr short MAX_RECIPES_PER_ITEM = 10;
constexpr short MAX_CRAFTING_BLOCKS = 20;
// ====================== 合成表类 ======================
class ItemTable {
public:
enum PathMode : uint8_t { SHORTEST, LONGEST, EXACT };
enum BatchMode : uint8_t { EXACT_BATCH, MERGE_BATCH };
private:
CraftGrid grid;
vector<pair<string, short>> outputs; // 多输出槽支持
string blockID; // 关联的合成方块ID
short craftCount = 1; // 每次合成产出数量
public:
ItemTable() = default;
// 设置网格项
void setItem(int x, int y, const string& id, short count) noexcept {
if (x >= 0 && x < 3 && y >= 0 && y < 3) {
grid[x][y] = make_pair(id, count);
}
}
// 获取网格项
const pair<string, short>& getItem(int x, int y) const noexcept {
static const pair<string, short> empty = make_pair("", 0);
return (x >= 0 && x < 3 && y >= 0 && y < 3) ? grid[x][y] : empty;
}
// 设置输出槽
void setOutputSlot(size_t slot, const string& id, short count) {
if (slot >= outputs.size()) outputs.resize(slot + 1, make_pair("", 0));
outputs[slot] = make_pair(id, count);
}
// 获取输出槽
const pair<string, short>& getOutputSlot(size_t slot) const noexcept {
static const pair<string, short> empty = make_pair("", 0);
return (slot < outputs.size()) ? outputs[slot] : empty;
}
// 获取所有输出
const vector<pair<string, short>>& getAllOutputs() const noexcept { return outputs; }
// 获取主输出
pair<string, short> getMainOutput() const noexcept {
for (const auto& output : outputs) {
if (!output.first.empty() && output.second > 0) {
return output;
}
}
return make_pair("", 0);
}
// 设置关联的合成方块
void setBlockID(const string& id) noexcept { blockID = id; }
const string& getBlockID() const noexcept { return blockID; }
// 设置每次合成产出数量
void setCraftCount(short count) noexcept {
craftCount = max<short>(1, count);
}
short getCraftCount() const noexcept { return craftCount; }
// 移除对特定物品的引用
void removeReferencesTo(const string& id) noexcept {
// 清理输入网格
for (auto& row : grid) {
for (auto& slot : row) {
if (slot.first == id) {
slot.first = "";
slot.second = 0;
}
}
}
// 清理输出槽
for (auto& output : outputs) {
if (output.first == id) {
output.first = "";
output.second = 0;
}
}
}
// 获取路径描述
string getPathDescription() const {
ostringstream oss;
if (!blockID.empty()) oss << "[" << blockID << "] ";
const auto& mainOut = getMainOutput();
if (!mainOut.first.empty()) {
oss << mainOut.first << " x" << mainOut.second;
if (outputs.size() > 1) {
oss << " (+" << (outputs.size() - 1) << " 其他输出)";
}
}
return oss.str();
}
};
// ====================== 物品类 ======================
class Item {
string id;
short maxStack = DEFAULT_MAX_STACK;
vector<ItemTable> tables;
public:
explicit Item(string id, short maxStack = DEFAULT_MAX_STACK)
: id(move(id)), maxStack(maxStack) {
tables.reserve(MAX_RECIPES_PER_ITEM);
}
const string& getID() const noexcept { return id; }
short getMaxStack() const noexcept { return maxStack; }
// 添加合成表
void addTable(ItemTable table) {
if (tables.size() < MAX_RECIPES_PER_ITEM) {
tables.emplace_back(move(table));
}
}
// 获取所有合成表
const vector<ItemTable>& getTables() const noexcept { return tables; }
// 移除对特定物品的引用
void removeReferencesTo(const string& id) noexcept {
for (auto& table : tables) {
table.removeReferencesTo(id);
}
}
};
// ====================== 合成方块类 ======================
class CraftingBlock {
string id;
string name;
int inputSlots;
int outputSlots;
vector<ItemTable> recipes; // 关联的合成配方
public:
CraftingBlock(string id, string name, int inSlots, int outSlots)
: id(move(id)), name(move(name)), inputSlots(inSlots), outputSlots(outSlots) {
recipes.reserve(MAX_RECIPES_PER_ITEM);
}
// 添加配方
void addRecipe(const ItemTable& recipe) {
if (recipes.size() < MAX_RECIPES_PER_ITEM) {
recipes.push_back(recipe);
}
}
// 获取属性
const string& getID() const noexcept { return id; }
const string& getName() const noexcept { return name; }
int getInputSlots() const noexcept { return inputSlots; }
int getOutputSlots() const noexcept { return outputSlots; }
const vector<ItemTable>& getRecipes() const noexcept { return recipes; }
};
// ====================== 物品集合类 ======================
class ItemCollection {
unordered_map<string, Item> items;
unordered_map<string, CraftingBlock> craftingBlocks;
public:
// 添加物品
bool add(Item item) {
auto [it, inserted] = items.try_emplace(item.getID(), move(item));
return inserted;
}
// 获取物品
Item* getItem(const string& id) noexcept {
auto it = items.find(id);
return (it != items.end()) ? &it->second : nullptr;
}
const Item* getItem(const string& id) const noexcept {
auto it = items.find(id);
return (it != items.end()) ? &it->second : nullptr;
}
// 删除物品
bool removeItem(const string& id) {
auto it = items.find(id);
if (it == items.end()) return false;
// 移除所有引用
for (auto& [_, item] : items) {
item.removeReferencesTo(id);
}
// 从方块中移除引用
for (auto& [_, block] : craftingBlocks) {
for (auto& recipe : const_cast<vector<ItemTable>&>(block.getRecipes())) {
recipe.removeReferencesTo(id);
}
}
items.erase(it);
return true;
}
// 获取所有物品ID(排序后)
vector<string> getSortedIDs() const {
vector<string> ids;
ids.reserve(items.size());
for (const auto& [id, _] : items) ids.push_back(id);
sort(ids.begin(), ids.end());
return ids;
}
// 检查物品是否存在
bool contains(const string& id) const noexcept {
return items.find(id) != items.end();
}
// 物品数量
size_t size() const noexcept { return items.size(); }
// 添加合成方块
bool addCraftingBlock(CraftingBlock block) {
auto [it, inserted] = craftingBlocks.try_emplace(block.getID(), move(block));
return inserted;
}
// 获取合成方块
CraftingBlock* getCraftingBlock(const string& id) noexcept {
auto it = craftingBlocks.find(id);
return (it != craftingBlocks.end()) ? &it->second : nullptr;
}
// 获取所有合成方块
const unordered_map<string, CraftingBlock>& getAllCraftingBlocks() const noexcept {
return craftingBlocks;
}
// 删除合成方块
bool removeCraftingBlock(const string& id) {
auto it = craftingBlocks.find(id);
if (it == craftingBlocks.end()) return false;
// 移除所有关联的配方引用
for (auto& [_, item] : items) {
auto& tables = const_cast<vector<ItemTable>&>(item.getTables());
for (auto itTable = tables.begin(); itTable != tables.end(); ) {
if (itTable->getBlockID() == id) {
itTable = tables.erase(itTable);
} else {
++itTable;
}
}
}
craftingBlocks.erase(it);
return true;
}
// 文件存储
void saveToFile(const fs::path& path) const {
ofstream out(path, ios::binary);
if (!out) throw runtime_error("无法打开文件进行写入: " + path.string());
// 保存物品
size_t itemCount = items.size();
out.write(reinterpret_cast<const char*>(&itemCount), sizeof(itemCount));
for (const auto& [id, item] : items) {
// 保存ID
size_t len = id.size();
out.write(reinterpret_cast<const char*>(&len), sizeof(len));
out.write(id.c_str(), len);
// 保存属性
short maxStack = item.getMaxStack();
out.write(reinterpret_cast<const char*>(&maxStack), sizeof(maxStack));
// 保存合成表
size_t tableCount = item.getTables().size();
out.write(reinterpret_cast<const char*>(&tableCount), sizeof(tableCount));
for (const auto& table : item.getTables()) {
// 保存输入网格
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
const auto& slot = table.getItem(x, y);
len = slot.first.size();
out.write(reinterpret_cast<const char*>(&len), sizeof(len));
out.write(slot.first.c_str(), len);
out.write(reinterpret_cast<const char*>(&slot.second), sizeof(slot.second));
}
}
// 保存输出槽
const auto& outputs = table.getAllOutputs();
size_t outputCount = outputs.size();
out.write(reinterpret_cast<const char*>(&outputCount), sizeof(outputCount));
for (const auto& output : outputs) {
len = output.first.size();
out.write(reinterpret_cast<const char*>(&len), sizeof(len));
out.write(output.first.c_str(), len);
out.write(reinterpret_cast<const char*>(&output.second), sizeof(output.second));
}
// 保存关联方块ID
const string& blockID = table.getBlockID();
len = blockID.size();
out.write(reinterpret_cast<const char*>(&len), sizeof(len));
out.write(blockID.c_str(), len);
// 保存每次合成产出数量
short craftCount = table.getCraftCount();
out.write(reinterpret_cast<const char*>(&craftCount), sizeof(craftCount));
}
}
// 保存合成方块
size_t blockCount = craftingBlocks.size();
out.write(reinterpret_cast<const char*>(&blockCount), sizeof(blockCount));
for (const auto& [id, block] : craftingBlocks) {
// 保存ID
size_t len = id.size();
out.write(reinterpret_cast<const char*>(&len), sizeof(len));
out.write(id.c_str(), len);
// 保存名称
len = block.getName().size();
out.write(reinterpret_cast<const char*>(&len), sizeof(len));
out.write(block.getName().c_str(), len);
// 保存槽位
int slots = block.getInputSlots();
out.write(reinterpret_cast<const char*>(&slots), sizeof(slots));
slots = block.getOutputSlots();
out.write(reinterpret_cast<const char*>(&slots), sizeof(slots));
// 保存关联配方
size_t recipeCount = block.getRecipes().size();
out.write(reinterpret_cast<const char*>(&recipeCount), sizeof(recipeCount));
for (const auto& recipe : block.getRecipes()) {
// 保存配方数据
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
const auto& slot = recipe.getItem(x, y);
len = slot.first.size();
out.write(reinterpret_cast<const char*>(&len), sizeof(len));
out.write(slot.first.c_str(), len);
out.write(reinterpret_cast<const char*>(&slot.second), sizeof(slot.second));
}
}
const auto& outputs = recipe.getAllOutputs();
size_t outputCount = outputs.size();
out.write(reinterpret_cast<const char*>(&outputCount), sizeof(outputCount));
for (const auto& output : outputs) {
len = output.first.size();
out.write(reinterpret_cast<const char*>(&len), sizeof(len));
out.write(output.first.c_str(), len);
out.write(reinterpret_cast<const char*>(&output.second), sizeof(output.second));
}
const string& blockID = recipe.getBlockID();
len = blockID.size();
out.write(reinterpret_cast<const char*>(&len), sizeof(len));
out.write(blockID.c_str(), len);
short craftCount = recipe.getCraftCount();
out.write(reinterpret_cast<const char*>(&craftCount), sizeof(craftCount));
}
}
}
// 文件加载
void loadFromFile(const fs::path& path) {
ifstream in(path, ios::binary);
if (!in) throw runtime_error("无法打开文件进行读取: " + path.string());
items.clear();
craftingBlocks.clear();
// 加载物品
size_t itemCount;
in.read(reinterpret_cast<char*>(&itemCount), sizeof(itemCount));
for (size_t i = 0; i < itemCount; i++) {
// 加载ID
size_t len;
in.read(reinterpret_cast<char*>(&len), sizeof(len));
string id(len, ' ');
in.read(id.data(), len);
// 加载属性
short maxStack;
in.read(reinterpret_cast<char*>(&maxStack), sizeof(maxStack));
Item item(id, maxStack);
// 加载合成表
size_t tableCount;
in.read(reinterpret_cast<char*>(&tableCount), sizeof(tableCount));
for (size_t j = 0; j < tableCount; j++) {
ItemTable table;
// 加载输入网格
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
in.read(reinterpret_cast<char*>(&len), sizeof(len));
string id(len, ' ');
in.read(id.data(), len);
short cnt;
in.read(reinterpret_cast<char*>(&cnt), sizeof(cnt));
table.setItem(x, y, id, cnt);
}
}
// 加载输出槽
size_t outputCount;
in.read(reinterpret_cast<char*>(&outputCount), sizeof(outputCount));
for (size_t k = 0; k < outputCount; k++) {
in.read(reinterpret_cast<char*>(&len), sizeof(len));
string id(len, ' ');
in.read(id.data(), len);
short cnt;
in.read(reinterpret_cast<char*>(&cnt), sizeof(cnt));
table.setOutputSlot(k, id, cnt);
}
// 加载关联方块ID
in.read(reinterpret_cast<char*>(&len), sizeof(len));
string blockID(len, ' ');
in.read(blockID.data(), len);
table.setBlockID(blockID);
// 加载每次合成产出数量
short craftCount;
in.read(reinterpret_cast<char*>(&craftCount), sizeof(craftCount));
table.setCraftCount(craftCount);
item.addTable(move(table));
}
items.emplace(id, move(item));
}
// 加载合成方块
size_t blockCount;
in.read(reinterpret_cast<char*>(&blockCount), sizeof(blockCount));
for (size_t i = 0; i < blockCount; i++) {
// 加载ID
size_t len;
in.read(reinterpret_cast<char*>(&len), sizeof(len));
string id(len, ' ');
in.read(id.data(), len);
// 加载名称
in.read(reinterpret_cast<char*>(&len), sizeof(len));
string name(len, ' ');
in.read(name.data(), len);
// 加载槽位
int inSlots, outSlots;
in.read(reinterpret_cast<char*>(&inSlots), sizeof(inSlots));
in.read(reinterpret_cast<char*>(&outSlots), sizeof(outSlots));
CraftingBlock block(id, name, inSlots, outSlots);
// 加载关联配方
size_t recipeCount;
in.read(reinterpret_cast<char*>(&recipeCount), sizeof(recipeCount));
for (size_t j = 0; j < recipeCount; j++) {
ItemTable table;
// 加载输入网格
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
in.read(reinterpret_cast<char*>(&len), sizeof(len));
string id(len, ' ');
in.read(id.data(), len);
short cnt;
in.read(reinterpret_cast<char*>(&cnt), sizeof(cnt));
table.setItem(x, y, id, cnt);
}
}
// 加载输出槽
size_t outputCount;
in.read(reinterpret_cast<char*>(&outputCount), sizeof(outputCount));
for (size_t k = 0; k < outputCount; k++) {
in.read(reinterpret_cast<char*>(&len), sizeof(len));
string id(len, ' ');
in.read(id.data(), len);
short cnt;
in.read(reinterpret_cast<char*>(&cnt), sizeof(cnt));
table.setOutputSlot(k, id, cnt);
}
// 加载关联方块ID
in.read(reinterpret_cast<char*>(&len), sizeof(len));
string blockID(len, ' ');
in.read(blockID.data(), len);
table.setBlockID(blockID);
// 加载每次合成产出数量
short craftCount;
in.read(reinterpret_cast<char*>(&craftCount), sizeof(craftCount));
table.setCraftCount(craftCount);
block.addRecipe(table);
}
craftingBlocks.emplace(id, move(block));
}
}
};
// ====================== 材料计算引擎 ======================
class MaterialCalculator {
const ItemCollection& itemDB;
mutable unordered_map<string, int> depthCache;
public:
explicit MaterialCalculator(const ItemCollection& db) : itemDB(db) {}
// 计算材料需求
MaterialMap calculateMaterials(const string& id, long long count,
ItemTable::PathMode pathMode = ItemTable::SHORTEST,
ItemTable::BatchMode batchMode = ItemTable::EXACT_BATCH) {
MaterialMap materials;
set<string> visited;
CalcMaterialsRecursive(id, count, materials, visited, pathMode, batchMode);
return materials;
}
private:
void CalcMaterialsRecursive(const string& id, long long count, MaterialMap& materials,
set<string>& visited, ItemTable::PathMode pathMode,
ItemTable::BatchMode batchMode) {
// 检查循环依赖
if (visited.find(id) != visited.end()) return;
visited.insert(id);
// 获取物品
const Item* item = itemDB.getItem(id);
if (!item || item->getTables().empty()) {
materials[id] += count; // 基础材料
return;
}
// 选择最佳合成路径
const ItemTable* bestTable = selectBestTable(id, pathMode);
if (!bestTable) return;
// 计算合成批次
long long craftCnt = bestTable->getCraftCount();
long long batches = (batchMode == ItemTable::EXACT_BATCH) ?
(count + craftCnt - 1) / craftCnt :
static_cast<long long>(ceil(static_cast<double>(count) / craftCnt));
// 处理输入材料
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
const auto& slot = bestTable->getItem(x, y);
if (!slot.first.empty() && slot.second > 0) {
long long totalNeeded = batches * slot.second;
CalcMaterialsRecursive(slot.first, totalNeeded, materials, visited, pathMode, batchMode);
}
}
}
}
const ItemTable* selectBestTable(const string& id, ItemTable::PathMode mode) {
const Item* item = itemDB.getItem(id);
if (!item || item->getTables().empty()) return nullptr;
const ItemTable* bestTable = &item->getTables()[0];
if (mode == ItemTable::SHORTEST || mode == ItemTable::LONGEST) {
int bestDepth = (mode == ItemTable::SHORTEST) ? INT_MAX : 0;
for (const auto& table : item->getTables()) {
int depth = getCachedPathDepth(table);
if ((mode == ItemTable::SHORTEST && depth < bestDepth) ||
(mode == ItemTable::LONGEST && depth > bestDepth)) {
bestDepth = depth;
bestTable = &table;
}
}
}
return bestTable;
}
int getCachedPathDepth(const ItemTable& table) const {
int maxDepth = 0;
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
const auto& slot = table.getItem(x, y);
if (!slot.first.empty() && slot.second > 0) {
// 使用缓存查询
auto it = depthCache.find(slot.first);
if (it != depthCache.end()) {
maxDepth = max(maxDepth, it->second);
} else {
int depth = calculateItemDepth(slot.first);
depthCache[slot.first] = depth;
maxDepth = max(maxDepth, depth);
}
}
}
}
return maxDepth;
}
int calculateItemDepth(const string& id) const {
// 获取物品
const Item* item = itemDB.getItem(id);
if (!item || item->getTables().empty()) {
return 0; // 基础材料
}
// 计算最大深度
int maxDepth = 0;
for (const auto& table : item->getTables()) {
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
const auto& slot = table.getItem(x, y);
if (!slot.first.empty() && slot.second > 0) {
int depth = calculateItemDepth(slot.first) + 1;
maxDepth = max(maxDepth, depth);
}
}
}
}
return maxDepth;
}
};
// ====================== 逐步合成状态 ======================
struct CraftingStep {
string itemID;
long long required;
uint8_t recipeIndex;
bool completed;
};
class StepByStepCrafting {
const ItemCollection& db;
MaterialCalculator& calc;
string targetID;
long long targetCount;
stack<CraftingStep> stepStack;
unordered_set<string> synthesizedItems;
MaterialMap totalMaterials;
public:
StepByStepCrafting(const ItemCollection& db, MaterialCalculator& calc,
const string& id, long long count)
: db(db), calc(calc), targetID(id), targetCount(count) {}
// 开始逐步合成
void start() {
// 初始化第一个步骤
stepStack.push({targetID, targetCount, 0, false});
synthesizedItems.clear();
totalMaterials.clear();
// 开始交互
interactiveCrafting();
}
private:
// 交互式合成过程
void interactiveCrafting() {
while (!stepStack.empty()) {
CraftingStep& current = stepStack.top();
// 显示当前步骤信息
clear();
displayCurrentStep(current);
// 获取用户选择
int choice = getUserChoice();
// 处理用户选择
switch (choice) {
case 0: handleUndo(); break;
case 1: processCurrentStep(current); break;
case 2: stepStack.pop(); break; // 跳过
case 3: displayMaterialSummary(); pause(); break;
case 4: return; // 退出
default: cout << "无效选择!" << endl; wait(1);
}
}
cout << "恭喜! 所有材料已准备完成!" << endl;
pause();
}
void handleUndo() {
if (stepStack.size() > 1) {
CraftingStep current = stepStack.top();
stepStack.pop();
synthesizedItems.erase(current.itemID);
if (db.getItem(current.itemID) == nullptr ||
db.getItem(current.itemID)->getTables().empty()) {
totalMaterials[current.itemID] -= current.required;
}
} else {
cout << "已在第一步,无法返回!" << endl;
wait(1);
}
}
// 显示当前步骤信息
void displayCurrentStep(const CraftingStep& step) const {
cout << "===== 当前合成步骤 =====" << endl;
cout << "物品: " << step.itemID << " x " << step.required << endl;
const Item* item = db.getItem(step.itemID);
if (!item || item->getTables().empty()) {
cout << "\n这是基础材料,无法合成!" << endl;
return;
}
// 显示当前配方
const auto& table = item->getTables()[step.recipeIndex];
cout << "使用配方: " << table.getPathDescription() << endl;
// 显示当前材料
cout << "\n所需材料:" << endl;
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
const auto& slot = table.getItem(x, y);
if (!slot.first.empty() && slot.second > 0) {
long long totalNeeded = step.required * slot.second;
cout << " - " << slot.first << " x " << totalNeeded << endl;
}
}
}
// 显示当前状态
cout << "\n当前状态: " << (step.completed ? "已完成" : "待处理")
<< "\n========================" << endl;
}
// 获取用户选择
int getUserChoice() const {
cout << "\n选项:" << endl;
cout << "0. 返回上一步" << endl;
cout << "1. 合成此物品" << endl;
cout << "2. 跳过此物品" << endl;
cout << "3. 查看材料汇总" << endl;
cout << "4. 退出逐步合成" << endl;
cout << "选择: ";
int choice;
cin >> choice;
return choice;
}
// 处理当前步骤
void processCurrentStep(CraftingStep& step) {
const Item* item = db.getItem(step.itemID);
if (!item || item->getTables().empty()) {
// 基础材料,直接完成
totalMaterials[step.itemID] += step.required;
stepStack.pop();
return;
}
const auto& table = item->getTables()[step.recipeIndex];
short craftCount = table.getCraftCount();
long long batches = (step.required + craftCount - 1) / craftCount; // 整数除法
// 添加子步骤
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
const auto& slot = table.getItem(x, y);
// 跳过空槽位和已合成物品
if (slot.first.empty() || slot.second == 0 ||
synthesizedItems.find(slot.first) != synthesizedItems.end()) continue;
// 创建新步骤
stepStack.push({slot.first, batches * slot.second, 0, false});
}
}
// 标记当前步骤为已完成
step.completed = true;
synthesizedItems.insert(step.itemID);
}
// 显示材料汇总
void displayMaterialSummary() const {
clear();
cout << "===== 材料汇总 =====" << endl;
cout << "目标: " << targetID << " x " << targetCount << endl;
cout << "已准备材料:" << endl;
for (const auto& [id, cnt] : totalMaterials) {
cout << " - " << id << ": " << cnt << endl;
}
cout << "=====================" << endl;
}
};
// ====================== 用户界面工具 ======================
void clear() {
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
}
void pause() {
cout << "\n按回车键继续...";
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cin.get();
}
void wait(int seconds) {
this_thread::sleep_for(chrono::seconds(seconds));
}
// ====================== 全局数据 ======================
ItemCollection itemDB;
MaterialCalculator calculator(itemDB);
fs::path savePath = fs::current_path() / "items.dat";
// ====================== 文件操作 ======================
void SaveItems(bool showMessage = true) {
try {
if (!fs::exists(savePath.parent_path())) {
fs::create_directories(savePath.parent_path());
}
itemDB.saveToFile(savePath);
if (showMessage) cout << "物品数据已保存!" << endl;
} catch (const exception& e) {
cerr << "保存错误: " << e.what() << endl;
}
}
void LoadItems() {
try {
if (fs::exists(savePath)) {
itemDB.loadFromFile(savePath);
cout << "物品数据已加载 (" << itemDB.size() << " 物品)" << endl;
} else {
cout << "未找到保存文件,创建新数据库" << endl;
}
} catch (const exception& e) {
cerr << "加载错误: " << e.what() << endl;
}
}
// ====================== 用户界面功能 ======================
void CreateItem() {
clear();
string id;
short maxStack;
cout << "物品ID: ";
cin >> id;
if (itemDB.contains(id)) {
cout << "物品已存在!" << endl;
pause();
return;
}
cout << "最大堆叠数 (默认64): ";
cin >> maxStack;
if (maxStack <= 0) maxStack = 64;
itemDB.add(Item(id, maxStack));
SaveItems(false);
cout << "物品创建成功!" << endl;
wait(1);
}
void DeleteItem() {
clear();
auto ids = itemDB.getSortedIDs();
if (ids.empty()) {
cout << "没有可删除的物品!" << endl;
pause();
return;
}
cout << "可用物品:\n";
for (const auto& id : ids) {
cout << " - " << id << "\n";
}
string target;
cout << "\n输入要删除的物品ID: ";
cin >> target;
if (!itemDB.contains(target)) {
cout << "物品不存在!" << endl;
pause();
return;
}
cout << "确认删除? (Y/N): ";
char confirm;
cin >> confirm;
if (toupper(confirm) == 'Y') {
if (itemDB.removeItem(target)) {
SaveItems(false);
cout << "物品已删除!" << endl;
} else {
cout << "删除失败!" << endl;
}
} else {
cout << "操作取消" << endl;
}
wait(1);
}
void AddRecipeToItem() {
clear();
auto ids = itemDB.getSortedIDs();
if (ids.empty()) {
cout << "请先创建物品!" << endl;
pause();
return;
}
cout << "可用物品:\n";
for (const auto& id : ids) {
cout << " - " << id << "\n";
}
string target;
cout << "\n输入要添加配方的物品ID: ";
cin >> target;
Item* item = itemDB.getItem(target);
if (!item) {
cout << "物品不存在!" << endl;
pause();
return;
}
// 选择合成方块
cout << "选择合成方块 (输入ID,留空使用默认): ";
string blockID;
cin.ignore();
getline(cin, blockID);
CraftingBlock* block = nullptr;
if (!blockID.empty()) {
block = itemDB.getCraftingBlock(blockID);
if (!block) {
cout << "方块不存在,使用默认合成" << endl;
wait(1);
}
}
// 创建新合成表
ItemTable newTable;
if (block) {
newTable.setBlockID(block->getID());
cout << "使用方块: " << block->getName() << endl;
}
// 设置输入网格(支持"0"表示空槽位)
cout << "\n输入3x3合成网格 (输入'0'表示空槽,格式: 物品ID 数量)\n";
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
string id;
short cnt;
cout << "位置 [" << x << "," << y << "]: ";
cin >> id;
// 处理"0"输入
if (id == "0") {
newTable.setItem(x, y, "", 0);
cin.ignore(numeric_limits<streamsize>::max(), '\n');
continue;
}
// 处理正常输入
cin >> cnt;
if (!itemDB.contains(id)) {
cout << "物品不存在,跳过此位置!" << endl;
newTable.setItem(x, y, "", 0);
continue;
}
newTable.setItem(x, y, id, cnt);
}
}
// 设置每次合成产出数量
short craftCount;
cout << "每次合成产出数量: ";
cin >> craftCount;
newTable.setCraftCount(craftCount > 0 ? craftCount : 1);
// 设置输出槽数量
int outputSlots = block ? block->getOutputSlots() : 1;
cout << "\n设置输出槽 (共 " << outputSlots << " 个槽位)\n";
for (int i = 0; i < outputSlots; i++) {
string outID;
short outCnt;
cout << "输出槽 #" << (i+1) << " (物品ID 数量,留空跳过): ";
cin >> outID;
if (outID.empty() || outID == "0") {
newTable.setOutputSlot(i, "", 0);
cin.ignore(numeric_limits<streamsize>::max(), '\n');
continue;
}
cin >> outCnt;
if (!itemDB.contains(outID)) {
cout << "物品不存在,跳过此输出槽!" << endl;
newTable.setOutputSlot(i, "", 0);
continue;
}
newTable.setOutputSlot(i, outID, outCnt);
}
// 添加到物品或方块
if (block) {
block->addRecipe(newTable);
} else {
item->addTable(newTable);
}
SaveItems(false);
cout << "合成表添加成功!" << endl;
wait(1);
}
void ViewRecipes() {
clear();
auto ids = itemDB.getSortedIDs();
if (ids.empty()) {
cout << "没有可查看的物品!" << endl;
pause();
return;
}
cout << "可用物品:\n";
for (size_t i = 0; i < ids.size(); i++) {
cout << setw(2) << i+1 << ". " << ids[i] << "\n";
}
int choice;
cout << "\n选择物品 (0返回): ";
cin >> choice;
if (choice <= 0 || choice > static_cast<int>(ids.size())) return;
string target = ids[choice-1];
Item* item = itemDB.getItem(target);
if (!item) return;
clear();
cout << "物品: " << target << "\n";
cout << "最大堆叠: " << item->getMaxStack() << "\n\n";
if (item->getTables().empty()) {
cout << "此物品没有合成配方!" << endl;
pause();
return;
}
cout << "合成配方:\n";
for (size_t i = 0; i < item->getTables().size(); i++) {
const auto& table = item->getTables()[i];
cout << "配方 #" << i+1 << ": " << table.getPathDescription() << "\n";
}
cout << "\n输入配方编号查看详情 (0返回): ";
cin >> choice;
if (choice <= 0 || choice > static_cast<int>(item->getTables().size())) return;
const auto& table = item->getTables()[choice-1];
clear();
cout << "配方详情: " << table.getPathDescription() << "\n";
// 显示输入网格
cout << "\n输入网格:\n";
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
const auto& slot = table.getItem(x, y);
cout << "[" << (slot.first.empty() ? " " : slot.first.substr(0,2))
<< (slot.second > 0 ? to_string(slot.second) : " ") << "] ";
}
cout << "\n";
}
// 显示输出槽
cout << "\n输出槽:\n";
for (size_t i = 0; i < table.getAllOutputs().size(); i++) {
const auto& slot = table.getOutputSlot(i);
if (!slot.first.empty() && slot.second > 0) {
cout << "槽位 #" << i+1 << ": " << slot.first << " x" << slot.second << "\n";
}
}
pause();
}
void AddCraftingBlock() {
clear();
string id, name;
int inSlots, outSlots;
cout << "方块ID: ";
cin >> id;
cout << "方块名称: ";
cin.ignore();
getline(cin, name);
cout << "输入槽数量: ";
cin >> inSlots;
cout << "输出槽数量: ";
cin >> outSlots;
if (inSlots <= 0 || outSlots <= 0) {
cout << "槽位数量必须大于0!" << endl;
pause();
return;
}
if (itemDB.getCraftingBlock(id)) {
cout << "方块ID已存在!" << endl;
pause();
return;
}
itemDB.addCraftingBlock(CraftingBlock(id, name, inSlots, outSlots));
SaveItems(false);
cout << "合成方块添加成功!" << endl;
wait(1);
}
void DeleteCraftingBlock() {
clear();
const auto& blocks = itemDB.getAllCraftingBlocks();
if (blocks.empty()) {
cout << "没有可用的合成方块!" << endl;
pause();
return;
}
cout << "可用合成方块:\n";
for (const auto& [id, block] : blocks) {
cout << " - " << id << " (" << block.getName() << ")\n";
}
string target;
cout << "\n输入要删除的方块ID: ";
cin >> target;
if (!itemDB.getCraftingBlock(target)) {
cout << "方块不存在!" << endl;
pause();
return;
}
cout << "确认删除? (Y/N): ";
char confirm;
cin >> confirm;
if (toupper(confirm) == 'Y') {
if (itemDB.removeCraftingBlock(target)) {
SaveItems(false);
cout << "方块已删除!" << endl;
} else {
cout << "删除失败!" << endl;
}
} else {
cout << "操作取消" << endl;
}
wait(1);
}
void CalculateMaterials() {
clear();
auto ids = itemDB.getSortedIDs();
if (ids.empty()) {
cout << "没有可计算的物品!" << endl;
pause();
return;
}
cout << "可用物品:\n";
for (size_t i = 0; i < ids.size(); i++) {
cout << setw(2) << i+1 << ". " << ids[i] << "\n";
}
int choice;
cout << "\n选择物品 (0返回): ";
cin >> choice;
if (choice <= 0 || choice > static_cast<int>(ids.size())) return;
string target = ids[choice-1];
long long count;
cout << "需要数量: ";
cin >> count;
// 选择计算模式
cout << "\n选择计算模式:\n"
<< "1. 最短路径 (默认)\n"
<< "2. 最长路径\n"
<< "3. 精确匹配\n"
<< "选择: ";
int modeChoice;
cin >> modeChoice;
ItemTable::PathMode pathMode = ItemTable::SHORTEST;
ItemTable::BatchMode batchMode = ItemTable::EXACT_BATCH;
if (modeChoice == 2) pathMode = ItemTable::LONGEST;
else if (modeChoice == 3) pathMode = ItemTable::EXACT;
// 计算材料
MaterialMap materials = calculator.calculateMaterials(target, count, pathMode, batchMode);
// 显示结果
clear();
cout << "合成 " << count << " 个 " << target << " 需要:\n";
cout << "================================\n";
long long totalItems = 0;
for (const auto& [id, cnt] : materials) {
cout << setw(15) << left << id << ": " << cnt << "\n";
totalItems += cnt;
}
cout << "================================\n";
cout << "总计材料: " << totalItems << " 个\n";
pause();
}
void StepByStepCraftingUI() {
clear();
auto ids = itemDB.getSortedIDs();
if (ids.empty()) {
cout << "没有可合成的物品!" << endl;
pause();
return;
}
cout << "可用物品:\n";
for (size_t i = 0; i < ids.size(); i++) {
cout << setw(2) << i+1 << ". " << ids[i] << "\n";
}
int choice;
cout << "\n选择物品 (0返回): ";
cin >> choice;
if (choice <= 0 || choice > static_cast<int>(ids.size())) return;
string target = ids[choice-1];
long long count;
cout << "需要数量: ";
cin >> count;
// 启动逐步合成
StepByStepCrafting stepper(itemDB, calculator, target, count);
stepper.start();
}
void SetSavePath() {
clear();
cout << "当前保存路径: " << savePath << "\n\n";
cout << "输入新路径: ";
string newPath;
cin.ignore();
getline(cin, newPath);
if (!newPath.empty()) {
savePath = fs::path(newPath);
cout << "路径已更新!" << endl;
wait(1);
}
}
// ====================== 主程序 ======================
int main() {
LoadItems();
while (true) {
clear();
cout << "===== 合成系统 =====" << endl;
cout << "1. 创建物品" << endl;
cout << "2. 删除物品" << endl;
cout << "3. 添加合成表" << endl;
cout << "4. 查看物品配方" << endl;
cout << "5. 材料计算" << endl;
cout << "6. 逐步合成" << endl;
cout << "7. 添加合成方块" << endl;
cout << "8. 删除合成方块" << endl;
cout << "9. 设置保存路径" << endl;
cout << "10. 保存数据" << endl;
cout << "11. 退出" << endl;
cout << "=====================" << endl;
cout << "物品数: " << itemDB.size()
<< " | 方块数: " << itemDB.getAllCraftingBlocks().size()
<< " | 路径: " << savePath.filename() << endl;
cout << "选项: ";
int choice;
cin >> choice;
switch (choice) {
case 1: CreateItem(); break;
case 2: DeleteItem(); break;
case 3: AddRecipeToItem(); break;
case 4: ViewRecipes(); break;
case 5: CalculateMaterials(); break;
case 6: StepByStepCraftingUI(); break;
case 7: AddCraftingBlock(); break;
case 8: DeleteCraftingBlock(); break;
case 9: SetSavePath(); break;
case 10: SaveItems(); pause(); break;
case 11:
SaveItems(false);
cout << "再见!" << endl;
return 0;
default:
cout << "无效选项!" << endl;
wait(1);
}
}
}
最新发布