bool CraftDefinitionShaped::check(const CraftInput &input, IGameDef *gamedef) const { if(input.method != CRAFT_METHOD_NORMAL) return false; // Get input item matrix std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef); unsigned int inp_width = input.width; if(inp_width == 0) return false; while(inp_names.size() % inp_width != 0) inp_names.push_back(""); // Get input bounds unsigned int inp_min_x=0, inp_max_x=0, inp_min_y=0, inp_max_y=0; if(!craftGetBounds(inp_names, inp_width, inp_min_x, inp_max_x, inp_min_y, inp_max_y)) return false; // it was empty // Get recipe item matrix std::vector<std::string> rec_names = craftGetItemNames(recipe, gamedef); unsigned int rec_width = width; if(rec_width == 0) return false; while(rec_names.size() % rec_width != 0) rec_names.push_back(""); // Get recipe bounds unsigned int rec_min_x=0, rec_max_x=0, rec_min_y=0, rec_max_y=0; if(!craftGetBounds(rec_names, rec_width, rec_min_x, rec_max_x, rec_min_y, rec_max_y)) return false; // it was empty // Different sizes? if(inp_max_x - inp_min_x != rec_max_x - rec_min_x) return false; if(inp_max_y - inp_min_y != rec_max_y - rec_min_y) return false; // Verify that all item names in the bounding box are equal unsigned int w = inp_max_x - inp_min_x + 1; unsigned int h = inp_max_y - inp_min_y + 1; for(unsigned int y=0; y<h; y++) for(unsigned int x=0; x<w; x++) { unsigned int inp_x = inp_min_x + x; unsigned int inp_y = inp_min_y + y; unsigned int rec_x = rec_min_x + x; unsigned int rec_y = rec_min_y + y; if(!inputItemMatchesRecipe( inp_names[inp_y * inp_width + inp_x], rec_names[rec_y * rec_width + rec_x], gamedef->idef()) ){ return false; } } return true; }
bool CraftDefinitionShapeless::check(const CraftInput &input, IGameDef *gamedef) const { if(input.method != CRAFT_METHOD_NORMAL) return false; // Get input item multiset std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef); std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names); // Get recipe item multiset std::vector<std::string> rec_names = craftGetItemNames(recipe, gamedef); std::multiset<std::string> rec_names_multiset = craftMakeMultiset(rec_names); // Recipe is matched when the multisets coincide return inp_names_multiset == rec_names_multiset; }
virtual bool getCraftResult(CraftInput &input, CraftOutput &output, bool decrementInput, IGameDef *gamedef) const { output.item = ""; output.time = 0; // If all input items are empty, abort. bool all_empty = true; for (std::vector<ItemStack>::const_iterator it = input.items.begin(); it != input.items.end(); it++) { if (!it->empty()) { all_empty = false; break; } } if (all_empty) return false; std::vector<std::string> input_names; input_names = craftGetItemNames(input.items, gamedef); std::sort(input_names.begin(), input_names.end()); // Try hash types with increasing collision rate, and return if found. for (int type = 0; type <= craft_hash_type_max; type++) { u64 hash = getHashForGrid((CraftHashType) type, input_names); /*errorstream << "Checking type " << type << " with hash " << hash << std::endl;*/ // We'd like to do "const [...] hash_collisions = m_craft_defs[type][hash];" // but that doesn't compile for some reason. This does. std::map<u64, std::vector<CraftDefinition*> >::const_iterator col_iter = (m_craft_defs[type]).find(hash); if (col_iter == (m_craft_defs[type]).end()) continue; const std::vector<CraftDefinition*> &hash_collisions = col_iter->second; // Walk crafting definitions from back to front, so that later // definitions can override earlier ones. for (std::vector<CraftDefinition*>::const_reverse_iterator it = hash_collisions.rbegin(); it != hash_collisions.rend(); it++) { CraftDefinition *def = *it; /*errorstream << "Checking " << input.dump() << std::endl << " against " << def->dump() << std::endl;*/ if (def->check(input, gamedef)) { // Get output, then decrement input (if requested) output = def->getOutput(input, gamedef); if (decrementInput) def->decrementInput(input, gamedef); /*errorstream << "Check RETURNS TRUE" << std::endl;*/ return true; } } } return false; }
void CraftDefinitionShaped::initHash(IGameDef *gamedef) { if (hash_inited) return; hash_inited = true; recipe_names = craftGetItemNames(recipe, gamedef); }
void CraftDefinitionShapeless::initHash(IGameDef *gamedef) { if (hash_inited) return; hash_inited = true; recipe_names = craftGetItemNames(recipe, gamedef); std::sort(recipe_names.begin(), recipe_names.end()); }
bool CraftDefinitionShapeless::check(const CraftInput &input, IGameDef *gamedef) const { if(input.method != CRAFT_METHOD_NORMAL) return false; // Filter empty items out of input std::vector<std::string> input_filtered; for(std::vector<ItemStack>::const_iterator i = input.items.begin(); i != input.items.end(); i++) { if(i->name != "") input_filtered.push_back(i->name); } // If there is a wrong number of items in input, no match if(input_filtered.size() != recipe.size()){ /*dstream<<"Number of input items ("<<input_filtered.size() <<") does not match recipe size ("<<recipe.size()<<") " <<"of recipe with output="<<output<<std::endl;*/ return false; } std::vector<std::string> recipe_copy; if (hash_inited) recipe_copy = recipe_names; else { recipe_copy = craftGetItemNames(recipe, gamedef); std::sort(recipe_copy.begin(), recipe_copy.end()); } // Try with all permutations of the recipe, // start from the lexicographically first permutation (=sorted), // recipe_names is pre-sorted do { // If all items match, the recipe matches bool all_match = true; //dstream<<"Testing recipe (output="<<output<<"):"; for(size_t i=0; i<recipe.size(); i++){ //dstream<<" ("<<input_filtered[i]<<" == "<<recipe_copy[i]<<")"; if(!inputItemMatchesRecipe(input_filtered[i], recipe_copy[i], gamedef->idef())){ all_match = false; break; } } //dstream<<" -> match="<<all_match<<std::endl; if(all_match) return true; }while(std::next_permutation(recipe_copy.begin(), recipe_copy.end())); return false; }
virtual bool getCraftResult(CraftInput &input, CraftOutput &output, std::vector<ItemStack> &output_replacement, bool decrementInput, IGameDef *gamedef) const { output.item = ""; output.time = 0; // If all input items are empty, abort. bool all_empty = true; for (const auto &item : input.items) { if (!item.empty()) { all_empty = false; break; } } if (all_empty) return false; std::vector<std::string> input_names; input_names = craftGetItemNames(input.items, gamedef); std::sort(input_names.begin(), input_names.end()); // Try hash types with increasing collision rate, and return if found. for (int type = 0; type <= craft_hash_type_max; type++) { u64 hash = getHashForGrid((CraftHashType) type, input_names); /*errorstream << "Checking type " << type << " with hash " << hash << std::endl;*/ // We'd like to do "const [...] hash_collisions = m_craft_defs[type][hash];" // but that doesn't compile for some reason. This does. auto col_iter = (m_craft_defs[type]).find(hash); if (col_iter == (m_craft_defs[type]).end()) continue; const std::vector<CraftDefinition*> &hash_collisions = col_iter->second; // Walk crafting definitions from back to front, so that later // definitions can override earlier ones. for (std::vector<CraftDefinition*>::size_type i = hash_collisions.size(); i > 0; i--) { CraftDefinition *def = hash_collisions[i - 1]; /*errorstream << "Checking " << input.dump() << std::endl << " against " << def->dump() << std::endl;*/ if (def->check(input, gamedef)) { // Check if the crafted node/item exists CraftOutput out = def->getOutput(input, gamedef); ItemStack is; is.deSerialize(out.item, gamedef->idef()); if (!is.isKnown(gamedef->idef())) { infostream << "trying to craft non-existent " << out.item << ", ignoring recipe" << std::endl; continue; } // Get output, then decrement input (if requested) output = out; if (decrementInput) def->decrementInput(input, output_replacement, gamedef); /*errorstream << "Check RETURNS TRUE" << std::endl;*/ return true; } } } return false; }