История изменений
Исправление rumgot, (текущая версия) :
var rules = {
"1XXXX" : 100,
"5XXXX" : 50,
"111XX" : 1000,
"AAAXX" : ({A})=>A * 100,
"1111X" : 2000,
"11111" : 4000,
"12345" : 8000,
"23456" : 8000,
"AABBX" : 4000
}
var token = new Set(["A","B"]); //возможные токены
var tokenAny = "X"; //токен любая кость
var diceValues = new Set(["1","2","3","4","5","6"]); //значения костей
//TODO ruleset validator
var dice = (d)=> Math.floor(Math.random() * d) + 1; //not used
var dices = (n, d)=> Array(n).fill().map(v=>dice(d)); //not used
var numSort = (a, b) => a - b; //сортировка чисел
var different = (arr) => { //все значения в массиве разные
arr.sort(numSort);
var res = true;
arr.forEach((v,i)=>{
if (!i) return;
if (v == arr[i - 1]) res = false;
});
return res;
}
var arrCount = (arr, what)=>{ //возвращает число вхождений в массив
return arr.reduce((s, v)=>(s + ((v === what) ? 1 : 0)), 0);
}
var arrRemove = (arr, what, count = 1) => { //убирает count what элементов из массива
for (let n = 0; n < count; n++) {
var i = arr.indexOf(what);
if (i == -1) return false;
arr.splice(i, 1);
}
return true;
}
var join = (arr, stack = [], i = 0)=>{ //декартово произведение массивов
if (!arr[i]) return stack;
return arr[i].map(v=>join(arr, [v, ...stack], i + 1))
}
var profit = function(rules, hand) { //подсчёт профита
if (!hand.length || hand.length != 6 || !hand.every(v=>diceValues.has(String(v))) throw("Type error in profit");
//var hand = [ 1, 3, 6, 2, 1]
var profits = Object.entries(rules).map(([rule, value])=>{
//var rule = "AABBX", value = 4000;
//rule - AABBX, 123XX, AA1111, value - number | function
var ruleDice = rule.split("").filter(v=>v != tokenAny); //AABB, 123, AA111
var tokens = new Set(ruleDice.filter(v=>token.has(v))); //AB, , A
var tokenCount = ruleDice.reduce((s, v)=>{ //{A : 2, B : 2}, {}, {A : 2}
s[v] = (s[v] || 0) + 1;
return s;
}, {})
var handSet = new Set(hand);
var variants =
join([...tokens].map(T=>[...handSet].map(D=>({[T] : D})))). //[ [ [ {A : 1}, {B : 1} ], [ {A : 1, B : 2} ], ...
flat(tokens.size - 1). //[ [ {A : 1}, {B : 1} ], [{A : 1}, {B : 2}] ...
filter(arr=>different(arr.map(v=>Object.values(v)[0]))). //[ [ {A : 1}, {B : 2} ], [{A : 1}, {B : 3}] ...
map(rec=>rec.reduce((s, v)=>({...s, ...v}), {})). // [ {A : 1, B : 2}, {A : 1, B : 3} ...
filter(v=>Object.entries(v).every(([T, D])=>tokenCount[T] <= arrCount(hand, D))) //[ { A : 1, B : 2 }, { A : 2, B : 1} ]
var func = (typeof value == "function") ? value : ()=>value; //приведение к функции
if (!variants.length && !tokens.size) {
variants = [{}]; //в случае если в правиле нет токенов A/B добавить заглушку
}
var ret = variants.map(variant=>{
var checkHand = [...hand];
var ok = true;
Object.values(variant).forEach(D=>{ //наличие нужного количества костей по токену правила
if (!ok) return;
var ok = arrRemove(checkHand, D, tokenConut[D]); //убираем из руки, если нашли что надо
})
if (!ok) return null;
ok = ruleDice.filter(v=>diceValues.has(v)).every(D=>{
var i = checkHand.indexOf(+D); //убираем кости по номеру в правилах
if (i == -1) return false;
checkHand.splice(i, 1);
return true;
});
if (!ok) return null;
return { hand, rule, variant, profit : func(variant) }
}).filter(v=>v);
return ret.reduce((max, v)=>{ //оставляем самый выигрышный вариант
if (max.profit < v.profit) max = v;
return max;
}, {hand, rule, profit : 0})
});
var maxProfit = profits.reduce((max, v)=>{ //еще раз
if (max.profit < v.profit) max = v;
return max;
}, { profit : 0 });
return maxProfit;
}
profit(rules, [1, 3, 6, 2, 1]) //100 - 1XXXX
profit(rules, [1, 5, 6, 2, 1]) //100 - 1XXXX
profit(rules, [3, 5, 6, 2, 6]) //50 - 5XXXX
profit(rules, [1, 1, 1, 2, 6]) //1000 - 111XX
profit(rules, [2, 2, 2, 3, 6]) //200 - AAAXX
profit(rules, [3, 3, 3, 1, 6]) //300 - AAAXX
profit(rules, [4, 4, 4, 3, 6]) //400 - AAAXX
profit(rules, [5, 5, 5, 3, 6]) //500 - AAAXX
profit(rules, [1, 1, 1, 1, 4]) //2000 - 1111X
profit(rules, [1, 1, 1, 1, 1]) //4000 - 11111
profit(rules, [1, 2, 4, 3, 5]) //8000 - 12345
profit(rules, [2, 3, 4, 5, 6]) //8000 - 23456
profit(rules, [2, 2, 3, 3, 6]) //4000 - AABBX
Мда… У тебя получилось плюс минус столько же строк (возможно в статье на хабре не все написано, ну даже если даписать, по большому счету кода будет примерно столько же).
Только вот мне, как человеку, который изредка немного пишет на JS (кстати, я думал, что «var» уже устарел) короткие скрипты-помогаторы для Qml и как человеку, который не писал на C# никогда, так вот мне код с хабра на C# читать легче, чем твой, код там более прост, названия классов говорящие и т.п. У тебя там сам черт ногу сломит. И это как раз отличное доказательство моих утверждений: если язык позволяет писать лапшой и венегретом - то так и будут делать. И вот конечно же ты никогда не ошибешься и не обратишься к несуществующему методу или переменной-члену. А если такого кода 100k+ строк ? Тоже нет? Ну что же, ты исключение, но большинство программистов ошибутся. А когда через пару лет нужно будет разбираться в таком коде, что тогда?
Справедливости ради замечу, что наверное на JS можно было бы сделать более читабельнее, но это не исключает проблему того, что где-то может быть несоответствие типов/вызываемых методов.
Исходная версия rumgot, :
var rules = {
"1XXXX" : 100,
"5XXXX" : 50,
"111XX" : 1000,
"AAAXX" : ({A})=>A * 100,
"1111X" : 2000,
"11111" : 4000,
"12345" : 8000,
"23456" : 8000,
"AABBX" : 4000
}
var token = new Set(["A","B"]); //возможные токены
var tokenAny = "X"; //токен любая кость
var diceValues = new Set(["1","2","3","4","5","6"]); //значения костей
//TODO ruleset validator
var dice = (d)=> Math.floor(Math.random() * d) + 1; //not used
var dices = (n, d)=> Array(n).fill().map(v=>dice(d)); //not used
var numSort = (a, b) => a - b; //сортировка чисел
var different = (arr) => { //все значения в массиве разные
arr.sort(numSort);
var res = true;
arr.forEach((v,i)=>{
if (!i) return;
if (v == arr[i - 1]) res = false;
});
return res;
}
var arrCount = (arr, what)=>{ //возвращает число вхождений в массив
return arr.reduce((s, v)=>(s + ((v === what) ? 1 : 0)), 0);
}
var arrRemove = (arr, what, count = 1) => { //убирает count what элементов из массива
for (let n = 0; n < count; n++) {
var i = arr.indexOf(what);
if (i == -1) return false;
arr.splice(i, 1);
}
return true;
}
var join = (arr, stack = [], i = 0)=>{ //декартово произведение массивов
if (!arr[i]) return stack;
return arr[i].map(v=>join(arr, [v, ...stack], i + 1))
}
var profit = function(rules, hand) { //подсчёт профита
if (!hand.length || hand.length != 6 || !hand.every(v=>diceValues.has(String(v))) throw("Type error in profit");
//var hand = [ 1, 3, 6, 2, 1]
var profits = Object.entries(rules).map(([rule, value])=>{
//var rule = "AABBX", value = 4000;
//rule - AABBX, 123XX, AA1111, value - number | function
var ruleDice = rule.split("").filter(v=>v != tokenAny); //AABB, 123, AA111
var tokens = new Set(ruleDice.filter(v=>token.has(v))); //AB, , A
var tokenCount = ruleDice.reduce((s, v)=>{ //{A : 2, B : 2}, {}, {A : 2}
s[v] = (s[v] || 0) + 1;
return s;
}, {})
var handSet = new Set(hand);
var variants =
join([...tokens].map(T=>[...handSet].map(D=>({[T] : D})))). //[ [ [ {A : 1}, {B : 1} ], [ {A : 1, B : 2} ], ...
flat(tokens.size - 1). //[ [ {A : 1}, {B : 1} ], [{A : 1}, {B : 2}] ...
filter(arr=>different(arr.map(v=>Object.values(v)[0]))). //[ [ {A : 1}, {B : 2} ], [{A : 1}, {B : 3}] ...
map(rec=>rec.reduce((s, v)=>({...s, ...v}), {})). // [ {A : 1, B : 2}, {A : 1, B : 3} ...
filter(v=>Object.entries(v).every(([T, D])=>tokenCount[T] <= arrCount(hand, D))) //[ { A : 1, B : 2 }, { A : 2, B : 1} ]
var func = (typeof value == "function") ? value : ()=>value; //приведение к функции
if (!variants.length && !tokens.size) {
variants = [{}]; //в случае если в правиле нет токенов A/B добавить заглушку
}
var ret = variants.map(variant=>{
var checkHand = [...hand];
var ok = true;
Object.values(variant).forEach(D=>{ //наличие нужного количества костей по токену правила
if (!ok) return;
var ok = arrRemove(checkHand, D, tokenConut[D]); //убираем из руки, если нашли что надо
})
if (!ok) return null;
ok = ruleDice.filter(v=>diceValues.has(v)).every(D=>{
var i = checkHand.indexOf(+D); //убираем кости по номеру в правилах
if (i == -1) return false;
checkHand.splice(i, 1);
return true;
});
if (!ok) return null;
return { hand, rule, variant, profit : func(variant) }
}).filter(v=>v);
return ret.reduce((max, v)=>{ //оставляем самый выигрышный вариант
if (max.profit < v.profit) max = v;
return max;
}, {hand, rule, profit : 0})
});
var maxProfit = profits.reduce((max, v)=>{ //еще раз
if (max.profit < v.profit) max = v;
return max;
}, { profit : 0 });
return maxProfit;
}
profit(rules, [1, 3, 6, 2, 1]) //100 - 1XXXX
profit(rules, [1, 5, 6, 2, 1]) //100 - 1XXXX
profit(rules, [3, 5, 6, 2, 6]) //50 - 5XXXX
profit(rules, [1, 1, 1, 2, 6]) //1000 - 111XX
profit(rules, [2, 2, 2, 3, 6]) //200 - AAAXX
profit(rules, [3, 3, 3, 1, 6]) //300 - AAAXX
profit(rules, [4, 4, 4, 3, 6]) //400 - AAAXX
profit(rules, [5, 5, 5, 3, 6]) //500 - AAAXX
profit(rules, [1, 1, 1, 1, 4]) //2000 - 1111X
profit(rules, [1, 1, 1, 1, 1]) //4000 - 11111
profit(rules, [1, 2, 4, 3, 5]) //8000 - 12345
profit(rules, [2, 3, 4, 5, 6]) //8000 - 23456
profit(rules, [2, 2, 3, 3, 6]) //4000 - AABBX
Мда… У тебя получилось плюс минус столько же строк (возможно в статье на хабре не все написано, ну даже если даписать, по большому счету кода будет примерно столько же).
Только вот мне, как человеку, который изредка немного пишет на JS (кстати, я думал, что «var» уже устарел) короткие скрипты-помогаторы для Qml и как человеку, который не писал на C# никогда, так вот мне код с хабра на C# читать легче, чем твой. У тебя там сам черт ногу сломит. И это как раз отличное доказательство моих утверждений: если язык позволяет писать лапшой и венегретом - то так и будут делать. И вот конечно же ты никогда не ошибешься и не обратишься к несуществующему методу или переменной-члену. А если такого кода 100k+ строк ? Тоже нет? Ну что же, ты исключение, но большинство программистов ошибутся. А когда через пару лет нужно будет разбираться в таком коде, что тогда?
Справедливости ради замечу, что наверное на JS можно было бы сделать более читабельнее, но это не исключает проблему того, что где-то может быть несоответствие типов/вызываемых методов.