2011-12-09 19 views
9

所以每個成分有4個效果 http://www.uesp.net/wiki/Skyrim:Ingredients什麼是生成天際(PC遊戲)藥水的所有可能組合的最有效方式?

如果我結合了兩種成分。這些藥水將會對兩組相交的地方產生獎勵效果。我不能兩次使用相同的成分。爲了生成所有2種成分的可能性,我只是列出了一對成分對。我將列表的頭部與列表中的每個元素進行比較,並在每次迭代中刪除頭部。這避免了愚蠢。

雖然我卡住了。我不知道如何生成3種成分組合,而不會受到任何困擾。有什麼建議麼?

回答

14

聽起來像每個人最喜歡的編程語言的工作,R

library(XML) 
tables <- readHTMLTable('http://www.uesp.net/wiki/Skyrim:Ingredients', 
    stringsAsFactors=FALSE) 
potions <- tables[[1]] 
twoway <- data.frame(t(combn(potions$Name,2))) 
threeway <- data.frame(t(combn(potions$Name,3))) 

BAM!

> head(twoway) 
       X1     X2 
1 Abecean Longfin   Bear Claws 
2 Abecean Longfin     Bee 
3 Abecean Longfin  Beehive Husk 
4 Abecean Longfin  Bleeding Crown 
5 Abecean Longfin   Blisterwort 
6 Abecean Longfin Blue Butterfly Wing 
> head(threeway) 
       X1   X2     X3 
1 Abecean Longfin Bear Claws     Bee 
2 Abecean Longfin Bear Claws  Beehive Husk 
3 Abecean Longfin Bear Claws  Bleeding Crown 
4 Abecean Longfin Bear Claws   Blisterwort 
5 Abecean Longfin Bear Claws Blue Butterfly Wing 
6 Abecean Longfin Bear Claws  Blue Dartwing 

使用write.csv命令將表保存爲csv文件。

/編輯:解釋我在做什麼:XML包中包含readHTMLTable函數,該函數將網站中的所有html表作爲data.frames並將其保存爲列表。這個列表中的第一個表是我們想要的。 combn函數查找魔藥名稱的所有2路,3路和n路combinations,並將結果作爲矩陣返回。我使用t函數轉置這個矩陣,所以每個組合都是一行,然後將其轉換爲數據框。這很容易擴展到n種成分的組合。

/編輯2:我寫了一個函數將n-way表保存到用戶指定的csv文件。我也重新做了一點,因爲轉換巨大的matricies在計算上花費很大。這個版本應該允許你計算4路表,雖然這需要很長時間,我不知道它是否與遊戲相關。

nway <- function(n, filepath, data=potions) { 
    nway <- combn(data$Name, n, simplify = FALSE) 
    nway <- do.call(rbind,nway) 
    write.csv(nway,filepath, row.names=FALSE) 
} 
nway(4,'~/Desktop/4way.csv') 

/編輯3:這裏有一些代碼來找到實際的工作藥水。這不是很有效率,可能會大大改善:

#Given an ingredient, lookup effects 
findEffects <- function(Name) { #Given a name, lookup effects 
    potions[potions$Name==Name,3:6] 
} 

#2-way potions 
intersectTwoEffects <- function(x) { 
    Effects1 <- findEffects(x[1]) 
    Effects2 <- findEffects(x[2]) 
    Effects <- unlist(intersect(Effects1,Effects2)) 
    Effects <- c(x[1],x[2],Effects) 
    length(Effects) <- 6 
    names(Effects) <- NULL 
    c(Effects,sum(is.na(Effects))) 

} 
twoway <- lapply(twoway,intersectTwoEffects) 
twoway <- do.call(rbind,twoway) 
twoway <- twoway[twoway[,7]<4,-7] #remove combos with no effect 
write.csv(twoway,'~/Desktop/twoway.csv',row.names=FALSE) 

#3-way potions 
intersectThreeEffects <- function(x) { 
    Effects1 <- findEffects(x[1]) 
    Effects2 <- findEffects(x[2]) 
    Effects3 <- findEffects(x[3]) 
    Effects <- c(intersect(Effects1,Effects2),intersect(Effects1,Effects3),intersect(Effects2,Effects3)) 
    Effects <- unlist(unique(Effects)) 
    Effects <- c(x[1],x[2],x[3],Effects) 
    length(Effects) <- 8 
    names(Effects) <- NULL 
    c(Effects,sum(is.na(Effects))) 

} 
threeway <- lapply(threeway,intersectThreeEffects) 
threeway <- do.call(rbind,threeway) 
threeway <- threeway[threeway[,9]<5,-9] #remove combos with no effect 
write.csv(threeway,'~/Desktop/threeway.csv',row.names=FALSE) 
+1

(+1)尼斯響應。 – chl

+0

+1對於一個有用的答案,但生成每個組合實際上是一種有效的方式來找到工作藥水食譜? –

+0

@David B:你認爲哪種方法更有效? – Zach

4

這裏是一些c#。

它通過潛在影響的名稱查找成分。然後它使用該查找來確定哪些配料可以匹配當前配方。最後,它會生成食譜和丟棄重複項,因爲它使用哈希集生成它們。

完整代碼(不完整的成分列表)

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace Combinations 
{ 

    public class Ingredient 
    { 
     public List<string> Effects { get; set; } 
     public string Name { get; set; } 
     public Ingredient(string name, params string[] effects) 
     { Name = name; Effects = new List<string>(effects); } 
    } 

    public class Recipe 
    { 
     public List<Ingredient> Ingredients {get;set;} 
     public Recipe(IEnumerable<Ingredient> ingredients) 
     { Ingredients = ingredients.OrderBy(x => x.Name).ToList(); } 
     public override string ToString() 
     { return string.Join("|", Ingredients.Select(x => x.Name).ToArray()); } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      List<Ingredient> source = GetIngredients(); 

      ILookup<string, Ingredient> byEffect = (
       from i in source 
       from e in i.Effects 
       select new { i, e } 
       ).ToLookup(x => x.e, x => x.i); 

      List<Recipe> oneIng = source.Select(x => new Recipe(new Ingredient[] { x })).ToList(); 
      List<Recipe> twoIng = oneIng.SelectMany(r => GenerateRecipes(r, byEffect)).ToList(); 
      List<Recipe> threeIng = twoIng.SelectMany(r => GenerateRecipes(r, byEffect)).ToList(); 

      Console.WriteLine(twoIng.Count); 
      foreach(Recipe r in twoIng) { Console.WriteLine(r); } 
      Console.WriteLine(threeIng.Count); 
      foreach(Recipe r in threeIng) { Console.WriteLine(r); } 
      Console.ReadLine(); 
     } 

     static IEnumerable<Recipe> GenerateRecipes(Recipe recipe, ILookup<string, Ingredient> byEffect) 
     { 
      IEnumerable<string> knownEffects = recipe.Ingredients 
       .SelectMany(i => i.Effects) 
       .Distinct(); 

      IEnumerable<Ingredient> matchingIngredients = knownEffects 
       .SelectMany(e => byEffect[e]) 
       .Distinct() 
       .Where(i => !recipe.Ingredients.Contains(i)); 

      foreach(Ingredient i in matchingIngredients) 
      { 
       List<Ingredient> newRecipeIngredients = recipe.Ingredients.ToList(); 
       newRecipeIngredients.Add(i); 
       Recipe result = new Recipe(newRecipeIngredients); 
       string key = result.ToString(); 
       if (!_observedRecipes.Contains(key)) 
       { 
        _observedRecipes.Add(key); 
        yield return result; 
       } 
      } 
     } 

     static HashSet<string> _observedRecipes = new HashSet<string>(); 

     static List<Ingredient> GetIngredients() 
     { 
      List<Ingredient> result = new List<Ingredient>() 
      { 
       new Ingredient("Abecean Longfin", "Weakness to Frost", "Fortify Sneak", "Weakness to Poison", "Fortify Restoration"), 
       new Ingredient("Bear Claws", "Restore Stamina", "Fortify Health", "Fortify One-handed", "Damage Magicka Regen"), 
       new Ingredient("Bee", "Restore Stamina", "Ravage Stamina", "Regenerate Stamina", "Weakness to Shock"), 
       new Ingredient("Beehive Husk", "Resist Poison", "Fortify Light Armor", "Fortify Sneak", "Fortify Destruction"), 
       new Ingredient("Bleeding Crown", "Weakness to Fire", "Fortify Block", "Weakness to Poison", "Resist Magic"), 
       new Ingredient("Blisterwort", "Damage Stamina", "Frenzy", "Restore Health", "Fortify Smithing"), 
       new Ingredient("Blue Butterfly Wing", "Damage Stamina", "Fortify Conjuration", "Damage Magicka Regen", "Fortify Enchanting"), 
       new Ingredient("Blue Dartwing", "Resist Shock", "Fortify Pickpocket", "Restore Health", "Damage Magicka Regen"), 
       new Ingredient("Blue Mountain Flower", "Restore Health", "Fortify Conjuration", "Fortify Health", "Damage Magicka Regen"), 
       new Ingredient("Bone Meal", "Damage Stamina", "Resist Fire", "Fortify Conjuration", "Ravage Stamina"), 
      }; 

      return result; 
     } 
    } 
} 
1

所以我有思想,「什麼是最具成本效益的方式來獲得所有成分的知識?」即我希望在遊戲中知道所有成分的效果,但我不想花12個Daedra Hearts去做。

如果您使用傳統的搜索解決方案(A *等),分支因素是可怕的(有22000可能有效的藥水)。我嘗試了退火方法,但沒有得到好的結果。我最終去了一個明智的搜索;這是subobptimal,但它會完成工作。

這裏的進口和combinatorize代碼: 看跌期權「導入成分......」

fd = File::open('ingr_weighted.txt', 'r') 
dbtext = fd.read 
fd.close 
ingredients = [] 
cvg = [] 
id = 0 
dbtext.each_line { |line| 
    infos = line.split("\t") 
    ingredients << {:id => id, :name => infos[0], :effects => [infos[2],infos[3],infos[4],infos[5]], 
        :eff1 => infos[2], :eff2 => infos[3], :eff3 => infos[4], :eff4 => infos[5], 
        :weight => infos[6], :cost => infos[7].to_i+1} 
    id += 1 
    cvg << [false, false, false, false] 
} 


puts "Building potions..." 
potions = [] 
id = 0 
for a in 0..ingredients.length-2 
    for b in a+1..ingredients.length-1 
     # First try two-ingredient potions 
     uses = ingredients[a][:effects] & ingredients[b][:effects] 
     cost = ingredients[a][:cost] + ingredients[b][:cost] 
     if (uses.length > 0) 
      coverage = [ingredients[a][:effects].map{|x| uses.include? x}, 
         ingredients[b][:effects].map{|x| uses.include? x}] 
      potions << {:id => id, :effects => uses, :coverage => coverage, :ingredients => [a, b], :cost => cost} 
      id = id + 1 
     end 
     # Next create three-ingredient potions 
     for c in b+1..ingredients.length-1 
      uses = ingredients[a][:effects] & ingredients[b][:effects] | 
        ingredients[a][:effects] & ingredients[c][:effects] | 
        ingredients[b][:effects] & ingredients[c][:effects] 
      cost = ingredients[a][:cost] + ingredients[b][:cost] + ingredients[c][:cost] 
      if (uses.length > 0) 
       coverage = [ingredients[a][:effects].map{|x| uses.include? x}, 
          ingredients[b][:effects].map{|x| uses.include? x}, 
          ingredients[c][:effects].map{|x| uses.include? x}] 
       # Prune potions that contain a superfluous ingredient 
       if (coverage.inject(true) { |cum, cvgn| 
              cum = cum && cvgn.inject { |cum2,ef| cum2 = cum2 || ef} 
              }) 
        potions << {:id => id, :effects => uses, :coverage => coverage, :ingredients => [a,b,c], :cost => cost} 
        id = id + 1 
       end 
      end 
     end 
    end 
end 
# 22451 
puts "#{potions.count} potions generated!" 
puts "Searching..." 

輸入文件是拷貝pasta'd從維基之一,所以如果你使用一個mod或者你可以直接進入的東西。從這裏你可以導入所有的數據並生成有效的魔藥,所以你想做什麼!

對於我最初的目的(高效的「學習」),我使用了下面的代碼。基本上它是以最昂貴的剩餘原料開始的,儘可能便宜地消耗其效果,然後繼續下去。一些罕見的成分便宜(外匯人肉),所以我「咕嚕」我的數據文件來人爲誇大其價值。總而言之,這個程序在我的筆記本電腦約45分鐘一班,但它一種解釋型語言...

相關問題