Skip to content

Commit 00824af

Browse files
mildmongrelmildmongrel
and
mildmongrel
authored
distribute bots more equally between players (#1694)
Modifies random bot placement to placement using a distribution algorithm to achieve more even placement in seats. Distribution function added to util.js to be independently testable from the Game class. Unit tests added. Co-authored-by: mildmongrel <[email protected]>
1 parent facff1e commit 00824af

File tree

3 files changed

+80
-10
lines changed

3 files changed

+80
-10
lines changed

backend/game.js

+14-10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const Rooms = require("./rooms");
1111
const logger = require("./logger");
1212
const Sock = require("./sock");
1313
const {saveDraftStats, getDataDir} = require("./data");
14+
const {distributeArrays} = require("./util");
1415

1516
module.exports = class Game extends Room {
1617
constructor({ hostId, title, seats, type, sets, cube, isPrivate, modernOnly, totalChaos, chaosPacksNumber, picksPerPack }) {
@@ -556,20 +557,23 @@ module.exports = class Game extends Room {
556557
Object.assign(this, { addBots, useTimer, timerLength, shufflePlayers });
557558
this.renew();
558559

559-
if (this.shouldAddBots()) {
560-
while (this.players.length < this.seats) {
561-
const burnsPerPack = this.type === "cube draft"
562-
? this.cube.burnsPerPack
563-
: 0;
564-
this.players.push(new Bot(this.picksPerPack, burnsPerPack, this.id));
565-
this.bots++;
566-
}
567-
}
568-
569560
if (shufflePlayers) {
570561
this.players = shuffle(this.players);
571562
}
572563

564+
if (this.shouldAddBots()) {
565+
const burnsPerPack = this.type === "cube draft"
566+
? this.cube.burnsPerPack
567+
: 0;
568+
569+
let bots = [];
570+
for (let i = 0; i < (this.seats - this.players.length); i++) {
571+
bots.push(new Bot(this.picksPerPack, burnsPerPack, this.id));
572+
}
573+
this.bots += bots.length;
574+
this.players = distributeArrays(this.players, bots);
575+
}
576+
573577
this.createPool();
574578

575579
if (/sealed/.test(this.type)) {

backend/util.js

+25
Original file line numberDiff line numberDiff line change
@@ -110,5 +110,30 @@ module.exports = {
110110
assert(typeof shufflePlayers === "boolean", "shufflePlayers must be a boolean");
111111
assert(useTimer && ["Fast", "Moderate", "Slow", "Leisurely"].includes(timerLength),
112112
"timerLength must be Fast, Moderate, Slow or Leisurely");
113+
},
114+
115+
// distributes elements in arr1 and arr2 as evenly as possible using a method
116+
// inspired by Bresenham's line algorithm
117+
distributeArrays(arr1, arr2) {
118+
assert(Array.isArray(arr1), "arr1 must be an array");
119+
assert(Array.isArray(arr2), "arr2 must be an array");
120+
121+
let arrX = (arr1.length >= arr2.length) ? arr1 : arr2;
122+
let arrY = (arr1.length >= arr2.length) ? arr2 : arr1;
123+
let out = [];
124+
125+
let diff = (2 * arrY.length) - arrX.length;
126+
let y = 0;
127+
for (let x = 0; x < arrX.length; x++) {
128+
if (diff > 0) {
129+
out.push( arrY[y] );
130+
y++;
131+
diff -= (2 * arrX.length);
132+
}
133+
out.push( arrX[x] );
134+
diff += (2 * arrY.length);
135+
}
136+
137+
return out;
113138
}
114139
};

backend/util.spec.js

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
const assert = require("assert");
2+
const util = require("./util");
3+
const { describe, it } = require("mocha");
4+
5+
describe("Acceptance tests for util functions", () => {
6+
describe("can distribute arrays", () => {
7+
it("produces a new array with each element from each argument", () => {
8+
// note this also tests degenerate cases with zero-element arrays
9+
for (let i = 0; i < 8; i++) {
10+
for (let j = 0; j < 8; j++) {
11+
const arr1 = Array(i).fill(1); // array of 1's
12+
const arr2 = Array(j).fill(2); // array of 2's
13+
14+
const out = util.distributeArrays( arr1, arr2 );
15+
assert.equal(out.length, i + j);
16+
assert(out.filter(i => i === 1).length == arr1.length);
17+
assert(out.filter(i => i === 2).length == arr2.length);
18+
19+
const outRev = util.distributeArrays( arr2, arr1 );
20+
assert.equal(outRev.length, i + j);
21+
assert(outRev.filter(i => i === 1).length == arr1.length);
22+
assert(outRev.filter(i => i === 2).length == arr2.length);
23+
}
24+
}
25+
});
26+
it("evenly distributes arrays with the same number of elements", () => {
27+
const arr1 = [0, 1, 2, 3];
28+
const arr2 = [10, 11, 12, 13];
29+
const out = util.distributeArrays( arr1, arr2 );
30+
assert.equal(out.length, 8);
31+
32+
// check that distance between arr1 elements is always even
33+
const firstIdx1 = out.indexOf(arr1[0]);
34+
for (let i = 0; i < arr1.length; ++i) {
35+
let idx = out.indexOf(arr1[i]);
36+
const dist = idx - firstIdx1;
37+
assert((dist % 2) == 0);
38+
}
39+
});
40+
});
41+
});

0 commit comments

Comments
 (0)