LINUX.ORG.RU

История изменений

Исправление 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 можно было бы сделать более читабельнее, но это не исключает проблему того, что где-то может быть несоответствие типов/вызываемых методов.