我需要存储数据来表示:
水+火=蒸汽
水+地球=泥浆
Mud + Fire = Rock
目标如下:我有可拖动的HTML div,当<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"},
最终修改:谢谢大家的贡献,我在您的所有评论中找到了非常有价值的信息
答案 0 :(得分:1)
每当您创建数据结构时,您都需要牢记以下事项:
我认为树的方式不是表示您尝试建模的数据的最佳方式。
然而,您在代码中展示的内容非常好。创建一个对象,其属性是方程式的右侧元素:
var stuff = {
Steam: ...,
Mud: ...,
Rock: ...
};
这允许在O(1)
时间内访问元素。
您可以将方程式的左侧建模为数组。这就是我本来会做的。
然而,将它们包装在额外的对象中只是额外的冗余,并且它增加了元素的访问时间。
我会像这样建模:
var stuff = {
Steam: ["Water", "Fire"],
Mud: ["Water", "Earth"],
Rock: ["Mud", "Fire"]
};
然后,您可以通过用各自的基本“东西”替换数组中复合“stuff”的所有出现来规范化表格。
修改:根据您的评论,我建议您使用如下数据结构(您可以并且应该将其存储在.json
文件中):
{
"Water": {
"needs": []
},
"Fire": {
"needs": []
},
"Earth": {
"needs": []
},
"Steam": {
"needs": ["Water", "Fire"]
},
"Mud": {
"needs": ["Water", "Earth"]
},
"Rock": {
"needs": ["Water", "Earth", "Fire"]
}
}
你应该存储基本内容而不是复合内容的原因是,复合内容的多个组合可以创建相同的stuff
。以最基本的形式存储成分是最不冗余的。
答案 1 :(得分: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。
答案 2 :(得分:1)
我玩Alchemy,我会用需要对象的链接替换needs数组。我还创建了一个make数组,我认为你在游戏中需要它。设置此深层链接一次可以使后续查找更快。
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());
}
答案 3 :(得分: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个参数:
"$.Value.needs"
)Key
)['Mud', Fire']
变为"Fire+Mud"
)。Get()
函数使用相同的转换函数找到其参数的匹配项。请注意,像"$.Value.needs"
这样的字符串参数是
的简写
function ($) { return $.Value.needs; }
。
linq.js还提供many more useful functions,可以将复杂的任务转换为单行。
编辑:从查找中返回所有其他信息将非常简单:
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()
是可选的。您可以删除它,在这种情况下,结果将与上一个示例中的结果相同。