- xml - AJAX/Jquery XML 解析
- 具有多重继承的 XML 模式
- .net - 枚举序列化 Json 与 XML
- XML 简单类型、简单内容、复杂类型、复杂内容
正在开发一款具有多种曲折的拉米风格游戏:
使用两副 5 组套牌而不是一组 4 组套牌(总共 116 张牌)。
套房从 3 到 King,每副牌有 3 张王牌(所以没有 2 也没有 A)。
11轮,第一轮每人3张牌,最后一轮每人13张牌。
除了 clown 是百搭外,每张牌的值(value)都会轮到百搭,这对应于您手中的牌数。
所以第一轮 3 是狂野的,第二轮 4 是狂野的……第 11 轮国王是狂野的(国王的数值为 13)。
目标是放下所有牌。一旦有人“出去”(放下所有牌),剩下的玩家就有一个回合放下所有牌或尽可能多的有效套牌/运行。无论您手中还剩下什么牌,您都会获得积分。
玩家只能在至少有 3 张牌的组或回合中放下牌,即 set: {3:c, 3:d, 3:h}
, 运行:{3:c, 4:c, 5:c}
。还有一些回合,您必须使用 3 张以上的牌来获得 set/run,因为手牌的数量不能被 3 整除。
为了开始手牌评估,我将卡片分成以下结构:
var suites = {
'clubs' : [],
'diamonds' : [],
'hearts' : [],
'spades' : [],
'stars' : []
};
var wilds = [];
var buckets = {
3 : [],
4 : [],
5 : [],
6 : [],
7 : [],
8 : [],
9 : [],
10 : [],
11 : [],
12 : [],
13 : []
};
//can have more then one run/set so these are used to hold valid runs/sets
var runs = [];
var sets = [];
var r_num = -1; //starts at -1 so ++ will give 0 index
var s_num = -1;
然后删除所有没有任何卡片的套房/桶。
集合评估非常简单,但运行评估......不是那么简单。
我想出了这个基于范围的运行评估
for (var s in suites){
if(suites.hasOwnProperty(s)){
var suite_length = suites[s].length;
if(suite_length >= 2){ //only evaluate suits with at least 2 cards in them
var range = suites[s][suite_length - 1].value - suites[s][0].value;
r_num++;
runs[r_num] = [];
if( (range - 1) >= wilds.length && (range - 1) == suite_length){
suites[s].forEach(function(card){ //large range needs several wilds
runs[r_num].push(card);
});
while(runs[r_num].length <= (range + 1) && wilds.length != 0){
runs[r_num].push(wilds.pop());
}
}else if(range == 1 && wilds.length >= 1){ //needs one wild
suites[s].forEach(function(card){
runs[r_num].push(card);
});
runs[r_num].push(wilds.pop());
}else if( range == suite_length && wilds.length >= 1){ //also needs one wild
suites[s].forEach(function(card){
runs[r_num].push(card);
});
runs[r_num].push(wilds.pop());
}else if( (range + 1) == suite_length){ //perfect run
suites[s].forEach(function(card){
runs[r_num].push(card);
});
}
}
}
}
};
这适用于很多情况,但很难解决套件中的重复问题。我在玩游戏测试时遇到的最棘手的情况之一如下:{3:梅花,百搭,3:黑桃,4:梅花,5:梅花,6:梅花,6:梅花 7:梅花,8:梅花,10:黑桃,10:红心,百搭
因此可以形成 12 张手牌和有效的下注:看起来像这样:集合:[{3:c, 3:s, wild},{10:s, 10:h, wild}]
运行:[{4:c, 5:c, 6:c}, {6:c, 7:c, 8:c}]
不幸的是,我最终得到了这样的结果:集合:[{6:c, 6:c, wild}, {10:s, 10:h, wild}]
运行:[{3:c,4:c,5:c}]
或(取决于我是先评估组还是先跑)集合:[{10:s, 10:h, wild, wild}]
运行:[{3:c, 4:c, 5:c, 6:c, 7:c, 8:c}]
剩下的其他卡片。
当我有这样的牌时,我也会在前几轮遇到问题:{6:h, 7:h, 7:h, 8:h}
以上是尝试放下部分手时的问题。{6:h, 7:h, 8:h}
是部分手牌情况下的有效运行,玩家将只剩下 7 分。但是因为我没有去除重复项,所以它不会将此评估为可能的运行,玩家将获得所有积分 (28)。
我的问题是: 是否有更好的方法/算法来完成整个过程?
我目前解决这个问题的想法有点令人沮丧,因为它们涉及大量针对特定手牌长度/游戏情况/套件长度的 case/if
语句。有时我删除重复项,有时我不删除,有时我不删除套件,有时我不删除……很多令人头疼的问题,基本上我不确定我是否涵盖了所有可能的手牌/部分放下的情况。
最佳答案
这是一个功能齐全的牌手评估器(尽管有一部分仍需要优化以提高效率)。它使用递归来检查所有可能的组合,并返回具有最低剩余值的组合。
卡片存储为 5 x 11 矩阵,值 0、1 或 2 代表该类型卡片的数量,如下所示:
3 4 5 6 w 8 9 T J Q K
CLB 1,0,0,0,2,0,1,0,0,0,0
DMD 0,0,1,0,1,0,1,0,0,0,0
HRT 0,0,0,0,0,0,1,1,1,0,0
SPD 0,1,0,0,1,0,0,0,0,0,0
STR 0,0,0,0,0,0,0,0,0,0,0
jokers: 1 value: 93
在此表示中,游程是非零的水平序列,集合是加起来等于或大于 3 的垂直线。
该函数通过搜索组合、创建手的副本、从副本中删除组合,然后在副本上调用自身来递归工作。
(实际上,递归是从矩阵的开头开始运行的;使用更好的算法来排列长组合的较短部分,递归可以从当前点开始运行,从而避免检查组合两次。)
该示例生成并评估一手随机的 13 张 K 牌。通过取消注释底部的代码,您可以输入特定的手牌进行检查。(数组克隆函数之后的所有代码只是运行示例并输出到控制台)。
// OBJECT HAND CONSTRUCTOR
function Hand(cards, jokers, round, wilds) {
this.cards = clone(cards);
this.jokers = jokers;
this.wilds = wilds || 0;
this.round = round;
this.melds = [];
this.value = this.leftoverValue();
}
// FIND MELDS AFTER CURRENT POINT
Hand.prototype.findMelds = function(suit, number, multi) {
if (suit == undefined || number == undefined || multi == undefined) {
// NOT A RECURSION
suit = number = multi = 0;
this.convertWilds();
}
// START WITH ONLY JOKERS AS OPTIMAL COMBINATION
if (this.jokers > 2) {
for (i = 0; i < this.jokers; i++) {
this.melds.push({s:-1, n:-1, m:-1});
}
this.value -= 25 * this.jokers - (22 - round) * (this.jokers < this.wilds ? this.jokers : this.wilds);
}
// SEARCH UNTIL END OR FULL LAY-DOWN
while (this.value > 0) {
// FIND NEXT CARD IN MATRIX
while (this.cards[suit][number] <= multi) {
multi = 0;
if (++number > 10) {
number = 0;
if (++suit > 4) return;
}
}
for (var type = 0; type < 2; type++) {
// FIND MELD AT CURRENT POINT
var meld = type ? this.findSet(suit, number, multi) : this.findRun(suit, number, multi);
// TRY DIFFERENT LENGTHS OF LONG MELD
// (to be improved: try permutations with jokers of each length)
for (var len = 3; len <= meld.length; len++) {
// CREATE COPY OF HAND AND REMOVE CURRENT MELD
var test = new Hand(this.cards, this.jokers, this.round, this.wilds);
test.removeCards(meld.slice(0, len));
// RECURSION ON THE COPY
// test.findMelds(suit, number, multi); // USE ONLY WITH MELD PERMUTATION ALGORITHM
test.findMelds(0,0,0); // RESULT OK, BUT MAY CHECK THINGS MULTIPLE TIMES
// BETTER COMBINATION FOUND
if (test.value < this.value) {
this.value = test.value;
while (this.melds.length) this.melds.pop();
for (var i = 0; i < len; i++) {
// ADD CURRENT MELD (DON'T DESTROY YET)
this.melds.push(meld[i]);
}
// ADD MELDS FOUND THROUGH RECURSION
while (test.melds.length) this.melds.push(test.melds.shift());
}
}
}
multi++;
}
}
// CHECK IF CARD IS START OF SET
Hand.prototype.findSet = function(s, n, m) {
var set = [];
while (s < 5) {
while (m < this.cards[s][n]) {
set.push({s:s, n:n, m:m++});
}
m = 0; s++;
}
// ADD JOKERS
for (i = 0; i < this.jokers; i++) {
set.push({s:-1, n:-1, m:-1});
}
return set;
}
// CHECK IF CARD IS START OF RUN
Hand.prototype.findRun = function(s, n, m) {
var run = [], jokers = this.jokers;
while (n < 11) {
if (this.cards[s][n] > m) {
run.push({s:s, n:n, m:m});
} else if (jokers > 0) {
run.push({s:-1, n:-1, m:-1});
jokers--;
}
else break;
m = 0; n++;
}
// ADD JOKERS (TRAILING OR LEADING)
while (jokers-- > 0 && run.length < 11) {
if (n++ < 11) run.push({s:-1, n:-1, m:-1});
else run.unshift({s:-1, n:-1, m:-1});
}
return run;
}
// REMOVE ARRAY OF CARDS FROM HAND: [{s:x, n:y} ... ]
Hand.prototype.removeCards = function(cards) {
for (var i = 0; i < cards.length; i++) {
if (cards[i].s >= 0) {
this.cards[cards[i].s][cards[i].n]--;
} else this.jokers--;
}
this.value = this.leftoverValue();
}
// GET VALUE OF LEFTOVER CARDS
Hand.prototype.leftoverValue = function() {
var leftover = 0;
for (var i = 0; i < 5; i++) {
for (var j = 0; j < 11; j++) {
leftover += this.cards[i][j] * (j + 3) // cards count from 0 vs 3
}
}
return leftover + 25 * this.jokers - (22 - round) * (this.jokers < this.wilds ? this.jokers : this.wilds);
}
// CONVERT WILDCARDS TO JOKERS
Hand.prototype.convertWilds = function() {
for (var i = 0; i < 5; i++) {
while (this.cards[i][this.round] > 0) {
this.cards[i][this.round]--;
this.jokers++; this.wilds++;
}
}
this.value = this.leftoverValue();
}
// UTILS: 2D ARRAY DEEP-COPIER
function clone(a) {
var b = [];
for (var i = 0; i < a.length; i++) {
b[i] = a[i].slice();
}
return b;
}
/* ******************************************************************************** */
// UTILS: SHOW HAND IN CONSOLE
function showHand(c, j, r, v) {
var num = " 3 4 5 6 7 8 9 T J Q K";
console.log(num.slice(0, 2*r+4) + "w" + num.slice(2*r+5));
for (var i = 0; i < 5; i++) {
console.log(["CLB ","DMD ","HRT ","SPD ","STR "][i] + c[i]);
}
console.log(" jokers: " + j + " value: " + v);
}
// UTILS: SHOW RESULT IN CONSOLE
function showResult(m, v) {
if (m.length == 0) console.log("no melds found");
while (m.length) {
var c = m.shift();
if (c.s == -1) console.log("joker *");
else console.log(["clubs","dmnds","heart","spade","stars"][c.s] + " " + "3456789TJQK".charAt(c.n));
}
console.log("leftover value: " + v);
}
// TEST DATA: CREATE ARRAY OF ALL CARDS TO DRAW FROM
var pack = [{s:-1,n:-1},{s:-1,n:-1},{s:-1,n:-1},{s:-1,n:-1},{s:-1,n:-1},{s:-1,n:-1}];
for (var i = 0; i < 5 ; i++) {
for (var j = 0; j < 11; j++) {
pack.push({s:i, n:j}, {s:i, n:j});
}
}
// TEST DATA: CREATE 2D ARRAY TO STORE CARDS
var matrix = [];
var jokers = 0;
var round = 10; // count from zero
for (var i = 0; i < 5; i++) {
matrix[i] = [];
for (var j = 0; j < 11; j++) {
matrix[i][j] = 0;
}
}
// TEST DATA: DRAW CARDS AND FILL 2D ARRAY
for (i = 0; i < round + 3; i++)
{
var j = Math.floor(Math.random() * pack.length);
var pick = pack[j];
pack[j] = pack.pop();
if (pick.s > -1) matrix[pick.s][pick.n]++;
else jokers++;
}
// USE THIS TO TEST SPECIFIC HANDS
// round = 10; // count from zero
// matrix = [[0,0,0,0,0,1,0,0,0,0,0],
// [0,0,0,0,0,1,0,0,0,0,0],
// [0,0,0,0,0,1,1,1,0,0,0],
// [0,0,0,0,0,1,0,0,0,0,0],
// [0,0,0,0,0,1,0,0,0,0,0]];
// jokers = 1;
// CREATE INSTANCE OF HAND OBJECT, EVALUATE AND SHOW RESULT
var x = new Hand(matrix, jokers, round); // no wilds parameter: automatic conversion
showHand(x.cards, x.jokers, x.round, x.value);
x.findMelds();
showResult(x.melds, x.value);
这是一个优化版本。集合的递归现在从当前点运行,运行的递归仍然从 0,0 运行。
将 findSets 函数优化到可以从当前点运行所有递归的点会增加复杂性,我认为这会取消任何可能的速度增益。
findRuns 和 findSets 函数现在返回运行和集合的变体数组;这也解决了运行中领先的 clown 的问题。
“multi”变量也已被删除,因为它只在一种特定情况下需要,这也可以通过对从 0,0 开始的集合运行递归来解决。
// CODE FOR SECOND VERSION REMOVED BECAUSE OF ANSWER LENGTH CONSTRAINTS
实际上,理论上“优化”的版本被证明只对多张双牌的手牌更快,而对于简单的手牌要慢得多,所以我决定制作一个混合版本。对于任何复杂的手牌,这比之前的两种算法都快 10%。
需要注意的是,为了简化代码,它在运行末尾添加了领先的 jokers,因此 *QK
运行将显示为 QK*
。
// OBJECT HAND CONSTRUCTOR
function Hand(cards, jokers, round, wilds) {
this.cards = clone(cards);
this.jokers = jokers;
this.wilds = wilds || 0;
this.round = round;
this.melds = [];
this.value = this.leftoverValue();
}
// FIND MELDS AFTER CURRENT POINT
Hand.prototype.findMelds = function(suit, number) {
if (suit == undefined || number == undefined) {
// NOT A RECURSION: CONVERT WILDS TO JOKERS
suit = number = 0;
this.convertWilds();
}
// START WITH ONLY JOKERS AS OPTIMAL COMBINATION
if (this.jokers > 2) {
for (var i = 0; i < this.jokers; i++) {
this.melds.push({s:-1, n:-1});
}
this.value -= 25 * this.jokers - (22 - round) * (this.jokers < this.wilds ? this.jokers : this.wilds);
}
// SEARCH UNTIL END OR FULL LAY-DOWN
while (this.value > 0) {
// FIND NEXT CARD IN MATRIX
while (number > 10 || this.cards[suit][number] == 0) {
if (++number > 10) {
number = 0;
if (++suit > 4) return;
}
}
// FIND RUNS OR SETS STARTING AT CURRENT POINT
for (var meldType = 0; meldType < 2; meldType++) {
var meld = meldType ? this.findSet(suit, number) : this.findRun(suit, number);
// TRY DIFFERENT LENGTHS OF LONG MELD
for (var len = 3; len <= meld.length; len++) {
// CREATE COPY OF HAND AND REMOVE CURRENT MELD
var test = new Hand(this.cards, this.jokers, this.round, this.wilds);
test.removeCards(meld.slice(0, len));
// RECURSION ON THE COPY
meldType ? test.findMelds(suit, number) : test.findMelds(0, 0);
// BETTER COMBINATION FOUND
if (test.value < this.value) {
this.value = test.value;
// REPLACE BEST COMBINATION BY CURRENT MELD + MELDS FOUND THROUGH RECURSION
this.melds.length = 0;
this.melds = [].concat(meld.slice(0, len), test.melds);
}
}
}
number++;
}
}
// FIND RUN STARTING WITH CURRENT CARD
Hand.prototype.findRun = function(s, n) {
var run = [], jokers = this.jokers;
while (n < 11) {
if (this.cards[s][n] > 0) {
run.push({s:s, n:n});
} else if (jokers > 0) {
run.push({s:-1, n:-1});
jokers--;
}
else break;
n++;
}
// ADD LEADING JOKERS (ADDED TO END FOR CODE SIMPLICITY)
while (jokers-- > 0) {
run.push({s:-1, n:-1});
}
return run;
}
// FIND SET STARTING WITH CURRENT CARD
Hand.prototype.findSet = function(s, n) {
var set = [];
while (s < 5) {
for (var i = 0; i < this.cards[s][n]; i++) {
set.push({s:s, n:n});
}
s++;
}
// ADD JOKERS
for (var i = 0; i < this.jokers; i++) {
set.push({s:-1, n:-1});
}
return set;
}
// REMOVE ARRAY OF CARDS FROM HAND
Hand.prototype.removeCards = function(cards) {
for (var i = 0; i < cards.length; i++) {
if (cards[i].s >= 0) {
this.cards[cards[i].s][cards[i].n]--;
} else this.jokers--;
}
this.value = this.leftoverValue();
}
// GET VALUE OF LEFTOVER CARDS
Hand.prototype.leftoverValue = function() {
var leftover = 0;
for (var i = 0; i < 5; i++) {
for (var j = 0; j < 11; j++) {
leftover += this.cards[i][j] * (j + 3) // cards count from 0 vs 3
}
}
return leftover + 25 * this.jokers - (22 - round) * (this.jokers < this.wilds ? this.jokers : this.wilds);
}
// CONVERT WILDCARDS TO JOKERS
Hand.prototype.convertWilds = function() {
for (var i = 0; i < 5; i++) {
while (this.cards[i][this.round] > 0) {
this.cards[i][this.round]--;
this.jokers++; this.wilds++;
}
}
this.value = this.leftoverValue();
}
// UTILS: 2D ARRAY DEEP-COPIER
function clone(a) {
var b = [];
for (var i = 0; i < a.length; i++) {
b[i] = a[i].slice();
}
return b;
}
// UTILS: SHOW HAND IN CONSOLE
function showHand(c, j, r, v) {
var num = " 3 4 5 6 7 8 9 T J Q K";
console.log(num.slice(0, 2*r+4) + "w" + num.slice(2*r+5));
for (var i = 0; i < 5; i++) {
console.log(["CLB ","DMD ","HRT ","SPD ","STR "][i] + c[i]);
}
console.log(" jokers: " + j + " value: " + v);
}
// UTILS: SHOW RESULT IN CONSOLE
function showResult(m, v) {
if (m.length == 0) console.log("no melds found");
while (m.length) {
var c = m.shift();
if (c.s == -1) console.log("joker *");
else console.log(["clubs","dmnds","heart","spade","stars"][c.s] + " " + "3456789TJQK".charAt(c.n));
}
console.log("leftover value: " + v);
}
// TEST DATA: CREATE ARRAY OF ALL CARDS TO DRAW FROM
var pack = [{s:-1,n:-1},{s:-1,n:-1},{s:-1,n:-1},{s:-1,n:-1},{s:-1,n:-1},{s:-1,n:-1}];
for (var i = 0; i < 5 ; i++) {
for (var j = 0; j < 11; j++) {
pack.push({s:i, n:j}, {s:i, n:j});
}
}
// TEST DATA: CREATE 2D ARRAY TO STORE CARDS
var matrix = [];
var jokers = 0;
var round = 10; // count from zero
for (var i = 0; i < 5; i++) {
matrix[i] = [];
for (var j = 0; j < 11; j++) {
matrix[i][j] = 0;
}
}
// TEST DATA: DRAW CARDS AND FILL 2D ARRAY
for (i = 0; i < round + 3; i++)
{
var j = Math.floor(Math.random() * pack.length);
var pick = pack[j];
pack[j] = pack.pop();
if (pick.s > -1) matrix[pick.s][pick.n]++;
else jokers++;
}
// USE THIS TO TEST SPECIFIC HANDS
// round = 10; // count from zero
// matrix = [[0,0,0,0,0,1,0,0,0,0,0],
// [0,0,0,0,0,2,1,1,0,0,0],
// [0,0,0,1,1,2,0,0,0,0,0],
// [0,0,0,0,0,1,0,0,0,0,0],
// [0,0,0,0,0,0,0,0,0,0,0]];
// jokers = 1;
// CREATE INSTANCE OF HAND OBJECT, EVALUATE AND SHOW RESULT
var x = new Hand(matrix, jokers, round); // no wilds parameter: automatic conversion
showHand(x.cards, x.jokers, x.round, x.value);
x.findMelds();
showResult(x.melds, x.value);
之前的版本并不是最优的,因为它们会多次检查某些组合。在尝试找到确保每个选项只检查一次的最佳方法时,我意识到运行和集合的不同性质需要两种不同的方法,并且查找集合不需要递归。最后,这是最优策略:
令我惊讶的是,尽管集合的代码有点繁琐,但对于复杂的手牌,该算法的速度提高了 10 倍以上。
// OBJECT HAND CONSTRUCTOR
function Hand(cards, jokers, round, wilds) {
this.cards = clone(cards);
this.jokers = jokers;
this.wilds = wilds || 0;
this.round = round;
this.melds = [];
this.value = this.leftoverValue();
}
// FIND MELDS FROM CURRENT POINT
Hand.prototype.findMelds = function(suit, number) {
// IF NOT A RECURSION: CONVERT WILDCARDS TO JOKERS AND START FROM 0,0
if (suit == undefined || number == undefined) {
suit = number = 0;
var noRecursion = true;
this.convertWilds();
}
// FIND CARDS FROM CURRENT POINT UNTIL END OR FULL LAY-DOWN
while (suit < 5 && this.value > 0) {
if (this.cards[suit][number] > 0) {
// FIND RUNS STARTING WITH CURRENT CARD
var run = this.findRun(suit, number);
// TRY DIFFERENT LENGTHS OF LONG RUN
for (var len = 3; len <= run.length; len++) {
// SKIP LONG RUNS ENDING WITH A JOKER
if (len > 3 && run[len - 1].s == -1) continue;
// CREATE COPY OF HAND, REMOVE RUN AND START RECURSION
var test = new Hand(this.cards, this.jokers, this.round, this.wilds);
test.removeCards(run.slice(0, len));
test.findMelds(suit, number);
// SAVE CURRENT RUN AND MELDS FOUND THROUGH RECURSION IF BETTER VALUE IS FOUND
if (test.value < this.value) {
this.value = test.value;
this.melds.length = 0;
this.melds = [].concat(run.slice(0, len), test.melds);
}
}
}
// MOVE THROUGH CARD MATRIX
if (++number > 10) {
number = 0;
++suit;
}
}
// AFTER ALL CARDS HAVE BEEN CHECKED FOR RUNS, CREATE COPY OF HAND AND FIND SETS
if (this.value > 0) {
var test = new Hand(this.cards, this.jokers, this.round, this.wilds);
test.findSets();
// SAVE SETS IF BETTER VALUE IS FOUND
if (test.value < this.value) {
this.value = test.value;
this.melds.length = 0;
while (test.melds.length) this.melds.push(test.melds.shift());
}
}
// FIX NO MELDS AND ONE JOKER EXCEPTION
if (noRecursion && this.melds.length < 3) {
this.melds.length = 0;
this.value = this.leftoverValue();
}
}
// FIND RUN STARTING WITH CURRENT CARD
Hand.prototype.findRun = function(s, n) {
var run = [], jokers = this.jokers;
while (n < 11) {
if (this.cards[s][n] > 0) {
run.push({s:s, n:n});
} else if (jokers > 0) {
run.push({s:-1, n:-1});
jokers--;
} else break;
n++;
}
// ADD LEADING JOKERS (AT END FOR CODE SIMPLICITY)
while (run.length < 3 && jokers--) {
run.push({s:-1, n:-1});
}
// REMOVE UNNECESSARY TRAILING JOKERS
while (run.length > 3 && run[run.length - 1].s == -1) {
run.pop();
}
return run;
}
// FIND SETS
Hand.prototype.findSets = function() {
var sets = [[], []], values = [[], []];
for (var n = 0; n < 11; n++) {
var set = [], value = 0;
for (var s = 0; s < 5; s++) {
for (var i = 0; i < this.cards[s][n]; i++) {
set.push({s:s, n:n});
value += n + 3;
}
}
// ADD COMPLETE SET TO MELDS, OR INCOMPLETE SET TO CANDIDATES TO RECEIVE JOKERS
if (set.length >= 3) {
this.addToMelds(set);
}
else if (set.length > 0) {
// STORE ONE-CARD SETS IN sets[0] AND TWO-CARD SETS IN sets[1]
sets[set.length - 1].push(set);
values[set.length - 1].push(value);
}
}
// IF TWO JOKERS ARE AVAILABLE: COMPLETE MOST VALUABLE TWO-CARD SET OR ONE-CARD SET(S)
while (this.jokers > 1 && sets[0].length > 0) {
var select = values[0][sets[0].length - 1];
for (var i = sets[1].length - 1; i >= 0 && i > sets[1].length - 3; i--) {
select -= values[1][i];
}
if (select > 0) {
set = sets[0].pop(); values[0].pop();
set.push({s:-1, n:-1}, {s:-1, n:-1});
this.addToMelds(set);
} else {
for (var i = 0; i < 2 && sets[1].length > 0; i++) {
set = sets[1].pop(); values[1].pop();
set.push({s:-1, n:-1});
this.addToMelds(set);
}
}
}
// IF JOKER IS AVAILABLE: COMPLETE MOST VALUABLE TWO-CARD SET
while (this.jokers > 0 && sets[1].length > 0) {
set = sets[1].pop();
set.push({s:-1, n:-1});
this.addToMelds(set);
}
// ADD LEFT-OVER JOKERS
while (this.jokers > 0) {
this.addToMelds([{s:-1, n:-1}]);
}
}
// ADD SET TO MELDS
Hand.prototype.addToMelds = function(set) {
this.removeCards(set);
while (set.length) this.melds.push(set.shift());
}
// REMOVE ARRAY OF CARDS FROM HAND
Hand.prototype.removeCards = function(cards) {
for (var i = 0; i < cards.length; i++) {
if (cards[i].s >= 0) {
this.cards[cards[i].s][cards[i].n]--;
} else this.jokers--;
}
this.value = this.leftoverValue();
}
// GET VALUE OF LEFTOVER CARDS
Hand.prototype.leftoverValue = function() {
var leftover = 0;
for (var i = 0; i < 5; i++) {
for (var j = 0; j < 11; j++) {
leftover += this.cards[i][j] * (j + 3) // cards count from 0 vs 3
}
}
return leftover + 25 * this.jokers - (22 - round) * (this.jokers < this.wilds ? this.jokers : this.wilds);
}
// CONVERT WILDCARDS TO JOKERS
Hand.prototype.convertWilds = function() {
for (var i = 0; i < 5; i++) {
while (this.cards[i][this.round] > 0) {
this.cards[i][this.round]--;
this.jokers++; this.wilds++;
}
}
this.value = this.leftoverValue();
}
// UTILS: 2D ARRAY DEEP-COPIER
function clone(a) {
var b = [];
for (var i = 0; i < a.length; i++) {
b[i] = a[i].slice();
}
return b;
}
// UTILS: SHOW HAND IN CONSOLE
function showHand(c, j, r, v) {
var num = " 3 4 5 6 7 8 9 T J Q K";
console.log(num.slice(0, 2*r+4) + "w" + num.slice(2*r+5));
for (var i = 0; i < 5; i++) {
console.log(["CLB ","DMD ","HRT ","SPD ","STR "][i] + c[i]);
}
console.log(" jokers: " + j + " value: " + v);
}
// UTILS: SHOW RESULT IN CONSOLE
function showResult(m, v) {
if (m.length == 0) console.log("no melds found");
while (m.length) {
var c = m.shift();
if (c.s == -1) console.log("joker *");
else console.log(["clubs","dmnds","heart","spade","stars"][c.s] + " " + "3456789TJQK".charAt(c.n));
}
console.log("leftover value: " + v);
}
// TEST DATA: CREATE ARRAY OF ALL CARDS TO DRAW FROM
var pack = [{s:-1,n:-1},{s:-1,n:-1},{s:-1,n:-1},{s:-1,n:-1},{s:-1,n:-1},{s:-1,n:-1}];
for (var i = 0; i < 5 ; i++) {
for (var j = 0; j < 11; j++) {
pack.push({s:i, n:j}, {s:i, n:j});
}
}
// TEST DATA: CREATE 2D ARRAY TO STORE CARDS
var matrix = [];
for (var i = 0; i < 5; i++) {
matrix[i] = [];
for (var j = 0; j < 11; j++) {
matrix[i][j] = 0;
}
}
// TEST DATA: DRAW CARDS AND FILL 2D ARRAY
var round = 10; // count from zero
var jokers = 0;
for (i = 0; i < round + 3; i++)
{
var j = Math.floor(Math.random() * pack.length);
var pick = pack[j];
pack[j] = pack.pop();
if (pick.s > -1) matrix[pick.s][pick.n]++;
else jokers++;
}
// USE THIS TO TEST SPECIFIC HANDS
// round = 10; // count from zero
// matrix = [[0,0,0,0,0,1,0,0,0,0,1],
// [0,0,0,0,0,1,0,0,0,0,0],
// [0,1,0,0,1,2,0,0,0,0,0],
// [0,0,0,0,0,2,1,0,0,1,1],
// [0,0,0,0,0,0,0,0,0,0,0]];
// jokers = 1;
// CREATE INSTANCE OF HAND OBJECT, EVALUATE AND SHOW RESULT
var x = new Hand(matrix, jokers, round); // CALL WITHOUT FOURTH (WILDS) PARAMETER
showHand(x.cards, x.jokers, x.round, x.value);
x.findMelds(); // CALL WITHOUT PARAMETERS TO INITIATE EVALUATION
showResult(x.melds, x.value);
关于javascript - 纸牌游戏手牌评估的组合学,带通配符和重复,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31615597/
我正在对一款流行游戏 (Minecraft) 进行一些修改,我在地形生成中看到了这些线条, double d4 = 1.0D; d4 *= d4; d4 *= d4; d4 = 1.0D - d4;
如何在没有浮点单元的处理器上执行 float 学运算?例如低端 8 位微 Controller 。 最佳答案 看看这篇文章:http://www.edwardrosten.com/code/fp_te
抱歉这个冗长的标题。我的代码针对没有浮点单元的微 Controller (msp430),但这应该适用于任何类似的 MCU。 如果我将一个大的运行时变量与通常被认为是浮点十进制数 (1.8) 相乘,M
我偶尔会在这个方法中遇到 stackoverflow 异常。 double norm_cdf(const double x) { double k = 1.0/(1.0 + 0.2316419
这是一个让我在过去几个小时里完全困惑的问题...... 我的程序中有一个硬编码的方程式: double s2; s2 = -(0*13)/84+6/42-0/84+24/12+(6*13)/42; 每
我知道 float 学充其量是丑陋的,但我想知道是否有人可以解释以下怪癖。在我测试的大多数编程语言中,将 0.4 添加到 0.2 会产生轻微错误,而 0.4 + 0.1 + 0.1 则不会。 两者计算
随着数据量持续增长,对合格数据专业人员的需求也会增长。具体而言,对SQL流利的专业人士的需求日益增长,而不仅仅是在初级层面。 因此,Stratascratch的创始人Nathan Rosidi以及我觉
当使用 c++ 或 -O0 编译时,以下 -O1 程序给出了数值不同的结果。 #include #include #include #include int main() { std::a
我正在尝试使用 SVG 在 map 上绘制飞行路径。我在 Leaflet 之上使用 d3,但所使用的框架不会对我的问题产生影响 - 这是三 Angular 关系。 http://fiddle.jshe
使用 IEEE754 float (在 JavaScript 中)时,与数学相关的精度损失风险是什么? 10*.1 即整数乘以有理数。 最佳答案 注意:该问题经过编辑,在发布此答案后很长时间添加了“t
我需要为网站 UI 做一些基本的 float 学运算(金钱的加法和乘法)。我知道 Javascript float 由于存储方式的原因并不准确,但我也知道以某种方式,可以获得我所需的准确度。我知道这一
我有一些像下面这样的宏: #define THING_LENGTH (512) #define MAX_COUNT (4096*8) #define MAX_LENGTH ((int32)((floa
我认为这是一个非常基本的问题 - 我正在执行此功能: private double convertMetersToFeet(double meters) { //function converts
我想在不损失太多精度的情况下替换这些函数中的 float 学,因为我没有 FPU。这可能吗?我认为逗号后的 3 个数字就足够了。 inline float smaller_f(float value,
我需要一个类来表示 double vector (在数学意义上)。 我需要的特殊功能: 任意维度 vector (我通常使用 10 - 100,000 维度) 高性能(用于受 CPU 限制的数字代码)
此社区 Wiki 问题的公认答案:What are best practices that you use when writing Objective-C and Cocoa?说 iPhone 不能
This question already has an answer here: Trouble with float on C [duplicate]
以下代码有问题: private const int movementMultiplier = 2; void Test() { XmlNode xnXCoordinate = xd.Sele
大家早上好 我在 float 学方面遇到了一些问题,完全迷失在“.to_f”、“*100”和“.0”中! 我希望有人能帮助我解决我的具体问题,并准确解释他们的解决方案为何有效,以便我下次理解这一点。
我的嵌入式 C 代码在具有单精度 FPU 的 Cortex M4F 上运行。我担心编译器多久将基于软件的 double 学放在诸如 ** float_var1 = 3.0 * int_var / fl
我是一名优秀的程序员,十分优秀!