1

これを表すデータを保存する必要があります。

水+火=蒸気
水+地球=
泥泥+火=岩

目標は次のとおりです。ドラッグ可能なHTMLdivがあり、オーバーラップするとき<div id="Fire">に画面<div id="Mud">に追加<div id="Rock">します。iPhoneまたはAndroidでAlchemyをプレイしたことがありますか?同じもの

今、私がこれを行っている方法はJSオブジェクトです:

var stuff = {
    'Steam' : { needs: [ 'Water', 'Fire'] },
    'Mud'   : { needs: [ 'Water', 'Earth'] },
    'Rock'  : { needs: [ 'Mud',   'Fire'] },
    // etc...
};

そして、divが別のdivとオーバーラップするたびに、オブジェクトキーをトラバースして、「needs」配列をチェックします。

私はその構造に対処することができますが、私はもっとうまくやれるかどうか疑問に思いましたか?

編集:簡単な説明やアイコン名など、他にもいくつか保存する必要があることを追加する必要があります。とても典型的な私はSteam: { needs: [ array ], desc: "short desc", icon:"steam.png"},

最終編集:貢献してくれてありがとうみんな、私はあなたのすべてのコメントで本当に貴重なインプットを見つけました

4

4 に答える 4

1

データ構造を作成するときは常に、次の点に注意する必要があります。

  1. エントロピーは高くなければなりません(つまり、冗長性が少なくなるはずです)。
  2. 要素のアクセス時間は短くする必要があります。

ツリーの見方は、モデル化しようとしているデータを表すための最良の方法ではありません。

ただし、コードで示したものは優れています。プロパティが方程式の右側の要素であるオブジェクトを作成します。

var stuff = {
    Steam: ...,
    Mud: ...,
    Rock: ...
};

これにより、要素にO(1)時間内にアクセスできます。

方程式の左辺は、これまでと同じように配列としてモデル化できます。それは私がしたであろうことです。

ただし、それらを追加のオブジェクトでラップすると、冗長性が増し、要素のアクセス時間が長くなります。

私はそれを次のようにモデル化したでしょう:

var stuff = {
    Steam: ["Water", "Fire"],
    Mud: ["Water", "Earth"],
    Rock: ["Mud", "Fire"]
};

次に、配列内の複合「スタッフ」のすべての出現箇所をそれぞれの基本「スタッフ」に置き換えることにより、テーブルを正規化できます。

編集:あなたのコメントから、私はあなたが次のようにデータ構造を使用することを提案します(あなたはそれを.jsonファイルに保存するかもしれませんし、そうすべきです):

{
    "Water": {
        "needs": []
    },
    "Fire": {
        "needs": []
    },
    "Earth": {
        "needs": []
    },
    "Steam": {
        "needs": ["Water", "Fire"]
    },
    "Mud": {
        "needs": ["Water", "Earth"]
    },
    "Rock": {
        "needs": ["Water", "Earth", "Fire"]
    }
}

複合的なものではなく基本的なものを保存する必要がある理由は、同じものがstuff複合的なものの複数の組み合わせによって作成される可能性があるためです。構成要素を基本形式で保存することは、最も冗長性が低くなります。

于 2012-11-07T15:57:44.960 に答える
1

他の依存関係を持つ要素とそれらが生成するものを直接コーディングするのはどうですか?

var elements = {
  water: {
    earth: 'mud', //water with earh produces mud
    fire: 'steam'
  },
  fire: {
    water: 'steam',
    mud: 'rock'
  },
  earth: {
    water: 'mud'
  },
  mud: {
    fire: 'rock'
  }
}

したがって、#div1と#div2がある場合は、次のようにします。

elements[div1][div2]
elements['fire']['water']
"steam"

生成された要素のIDを取得します。

于 2012-11-07T16:15:14.753 に答える
1

Alchemyをプレイしましたが、needs配列を必要なオブジェクトへのリンクに置き換えます。また、ゲームで必要になると思うmakes配列も作成しました。このディープリンクを一度設定すると、その後のルックアップがはるかに高速になります。

var stuff = {
  Fire: {
    name: 'Fire', active: true, needs: []
  }
  ,Water: {
    name: 'Water', active: true, needs: []
  }
  ,Earth: {
    name: 'Earth', active: true, needs: []
  }
  ,Steam: {
    name: 'Steam', active: false, needs: ['Water','Fire']
  }
  ,Mud: {
    name: 'Mud', active: false, needs: ['Water','Earth']
  }
  ,Rock: {
    name: 'Rock', active: false ,needs: ['Mud','Fire']
  }
};

for (var name in stuff) {
    // create links for needs and wants
    for (var i=0, n; n = stuff[name].needs[i]; i++) {
        if (stuff[n]) {
            stuff[name].needs[i] = stuff[n];
            if (!stuff[n].makes) stuff[n].makes = [];
            stuff[n].makes.push(stuff[name]);
        }
    }
    (function (o) {
        o.getNeeds = function () {
            var needs = [];
            for (var i=0, n; n = o.needs[i]; i++) {
                needs.push(o.needs[i].name);
            }
            return needs;
        };
        o.getMakes = function () {
            var makes = [];
            if (!o.makes) o.makes = [];
            for (var i=0, n; n = o.makes[i]; i++) {
                makes.push(o.makes[i].name);
            }
            return makes;
        };
        o.dump = function () {
            return o.name + " needs(" + o.getNeeds().join(',') + "), makes(" + o.getMakes().join(',') + ")";
        };
    })(stuff[name]);
}

stuff.testCombine = function (itemArray) {
    // itemArray is an unordered array of "stuff" names to test, eg ['Water','Fire']
    // if the elements in itemArray match an makes list for an item, this function returns that item.
    // if no combine was found, this function returns false
    if (!itemArray || !itemArray[0] || !stuff[itemArray[0]]) return false;

    // itemArray[0] is the guinea pig item, we see what it can make, and then see what the ingredient lists are and compare them to itemArray
    possible = stuff[itemArray[0]].makes;
    itemArray = itemArray.sort();

    for (var i=0, p; p = possible[i]; i++) {
        var n = p.getNeeds().sort();

        var matched = false;
        // check if n and itemArray are identical
        if (n.length && n.length == itemArray.length) {
          var j = 0;
          for (j=0; j < n.length && n[j] == itemArray[j]; j++);
          if (j == n.length) matched = true;
        }

        if (matched) return p;
    }
    return false;
}

// shows properties of Steam
alert(stuff.Steam.dump());
// shows properties of Water
alert(stuff.Water.dump());

alert("Water can be used to make :\n" + [stuff.Water.makes[0].dump(), stuff.Water.makes[1].dump()].join("\n"));

// stuff.Steam.needs[0] is Water, .makes[1] is Mud, .makes[0] is Rock
alert(stuff.Steam.needs[0].makes[1].makes[0].name);

// test if 'Water', 'Earth' makes something:
var m = stuff.testCombine(['Water','Earth']);
if (!m) { 
    alert('Did not Combine'); 
} else {
    alert('Combined to make ' + m.dump());
}
于 2012-11-07T16:49:16.847 に答える
1

別の外部ライブラリを含めることを気にせず、LINQに慣れている場合は、これにlinq.jsを使用できます。

var stuff = {
  'Steam' : { needs: ['Water', 'Fire'] },
  'Mud'   : { needs: ['Water', 'Earth'] },
  'Rock'  : { needs: ['Mud',   'Fire'] }
    // etc...
};

function Alchemy(stuff) {
  var recipes = 
    Enumerable.From(stuff).ToLookup(
      "$.Value.needs",
      "$.Key", 
      "Enumerable.From($).OrderBy().ToString('+')"
    );

  this.attempt = function(elem1, elem2) {
    return recipes.Get([elem1, elem2]).ToString();
  };
};

var alchemy = new Alchemy(stuff);
console.log(alchemy.attempt('Fire', 'Mud'));   // "Rock"
console.log(alchemy.attempt('Fire', 'Earth')); // ""
console.log(alchemy.attempt('Fire', 'Water')); // "Steam"

ノート

  • Enumerable.From(stuff)stuffオブジェクトをそのパーツKeyとパーツに分割しますValue
    たとえば、とKeyを参照します。"Rock"Value{ needs: ['Mud', 'Fire'] }
  • ToLookup()そこからルックアップ辞書を作成します。それは3つのパラメータを取ります:
    1. 何を探すか(この場合、の要素"$.Value.needs"
    2. 一致が見つかった場合に何を返すか(この場合、結果の要素の名前、つまりKey
    3. 辞書キーを作成する変換関数(この場合、材料の配列はソートされた文字列に変換されます:['Mud', Fire']になります"Fire+Mud")。
  • Get()関数は、同じ変換関数を使用して、引数に一致するものを見つけます。

のような文字列引数"$.Value.needs"は。の省略形であることに注意してください
function ($) { return $.Value.needs; }

linq.jsは、複雑なタスクをワンライナーに変換できる、より多くの便利な関数も提供します。


編集:ルックアップからすべての追加情報を返すのは、次のように簡単です。

function Alchemy(stuff) {
  var recipes = 
    Enumerable.From(stuff).ToLookup(
      "$.Value.needs",
      null, // return the object unchanged 
      "Enumerable.From($).OrderBy().ToString('+')"
    );

  this.attempt = function(elem1, elem2) {
    return recipes.Get([elem1, elem2]).FirstOrDefault();
  };
};

console.log(alchemy.attempt('Fire', 'Mud')); 
/* result
{
  Key: "Rock",
  Value: {
    needs: ["Mud", "Fire"],
    whatever: "else you had defined in {stuff}"
  }
}
*/

Lookupオブジェクトの目的は、速度を上げることです。毎回オブジェクトグラフ全体をトラバースすることもできます。

function alchemy(elem1, elem2) {
  return 
    Enumerable
    .From(stuff)
    .Where(function ($) {
      var recipe = Enumerable.From($.Value.needs);
      return recipe.Intersect([elem1, elem2]).Count() == 2;
    })
    .Select("{element: $.Key, properties: $.Value}")
    .FirstOrDefault();
);

console.log(alchemy('Fire', 'Water'));
// {element: "Steam", properties: {needs: ["Water", "Fire"]}}

.Select()これはオプションであることに注意してください。削除することもできます。その場合、結果は前の例と同じになります。

于 2012-11-09T22:58:33.903 に答える