Skip to content

Instantly share code, notes, and snippets.

@cympfh
Created September 2, 2018 05:41
Show Gist options
  • Save cympfh/03829fca99d3327eddb2963b5c2ec51b to your computer and use it in GitHub Desktop.
Save cympfh/03829fca99d3327eddb2963b5c2ec51b to your computer and use it in GitHub Desktop.
function iota(n, begin=0, step=1) {
var xs = [];
while (xs.length < n) {
if (xs.length == 0) {
xs.push(begin);
} else {
xs.push(xs[xs.length - 1] + step);
}
}
return xs
}
function shuffle(xs) {
return xs.map((x) => [Math.random(), x]).sort().map((pair) => pair[1]);
}
function choose(xs, n) {
let shuffled = shuffle(xs);
return shuffled.slice(0, n);
}
function intersection(xs, ys) {
var zs = [];
for (let x of xs) {
var ok = false;
for (let y of ys) {
if (x == y) {
ok = true;
break;
}
}
if (ok) {
zs.push(x);
}
}
return zs;
}
function setminus(xs, ys) {
var zs = [];
for (let x of xs) {
var ok = true;
for (let y of ys) {
if (x == y) {
ok = false;
break;
}
}
if (ok) {
zs.push(x);
}
}
return zs;
}
class Bingo {
constructor() {
this.A = [];
this.A = this.A.concat(choose(iota(15, 1), 5));
this.A = this.A.concat(choose(iota(15, 16), 5));
this.A = this.A.concat(choose(iota(15, 31), 5));
this.A = this.A.concat(choose(iota(15, 46), 5));
this.A = this.A.concat(choose(iota(15, 61), 5));
this.A = shuffle(this.A);
var rest = setminus(iota(75, 1), this.A);
this.c = choose(rest, 1)[0];
this.B = setminus(rest, [this.c]);
}
read() {
// 数字読み上げ列
return this.A.concat([this.c]);
}
sheet() {
// シートの発行
var cells = [];
var used = new Set();
for (var i = 0; i < 5; ++i) {
cells[i] = [];
for (var j = 0; j < 5; ++j) {
cells[i][j] = null;
}
}
cells[2][2] = 'FR'; // FREE cell
var c_x = choose(iota(5), 1)[0];
var c_y = Math.floor((this.c - 1) / 15); // 列方向は番号の範囲が決まってる
while (c_x == 2 && c_y == 2) {
c_x = choose(iota(5), 1)[0];
c_y = choose(iota(5), 1)[0];
}
cells[c_x][c_y] = this.c;
used.add(this.c);
// (c_x, c_y) を通るビンゴ列の列挙
var bingo_dirs = ['tate', 'yoko'];
if (c_x == c_y) bingo_dirs.push('rb');
if (c_x + c_y == 4) bingo_dirs.push('rt');
// bingo_dirs から 1 本または 2 本を選ぶ
let num = choose(iota(2, 1), 1)[0];
let dirs = choose(bingo_dirs, num);
// ビンゴ列のセルを集める
var A_area = [];
let vec = {
'tate': {x: 1, y: 0},
'yoko': {x: 0, y: 1},
'rb': {x: 1, y: 1},
'rt': {x: -1, y: 1}
};
for (let d of dirs) {
var x = c_x;
var y = c_y;
while (x >= 0 && y >= 0 && x < 5 && y < 5) {
if (cells[x][y] == null) {
A_area.push([x, y]);
}
x += vec[d].x;
y += vec[d].y;
}
x = c_x;
y = c_y;
while (x >= 0 && y >= 0 && x < 5 && y < 5) {
if (cells[x][y] == null) {
A_area.push([x, y]);
}
x -= vec[d].x;
y -= vec[d].y;
}
}
// A_area を A の数字で選ぶ
for (let cell of A_area) {
let x = cell[0];
let y = cell[1];
let cands = setminus(intersection(this.A, iota(15, 1 + 15 * y)), used);
cells[x][y] = choose(cands, 1)[0];
used.add(cells[x][y]);
}
// 予期しないビンゴ列に B を1つずつ入れる
for (var i = 0; i < 5; ++i) {
var cs = [];
for (var j = 0; j < 5; ++j) {
if (cells[i][j] == null) cs.push([i, j]);
}
if (cs.length > 0) {
let c = choose(cs, 1)[0];
let x = c[0];
let y = c[1];
cells[x][y] = choose(
intersection(
iota(15, 1 + 15 * y),
setminus(this.B, used)), 1)[0];
used.add(cells[x][y]);
}
}
for (var j = 0; j < 5; ++j) {
var cs = [];
for (var i = 0; i < 5; ++i) {
if (cells[i][j] == null) cs.push([i, j]);
}
if (cs.length > 0) {
let c = choose(cs, 1)[0];
let x = c[0];
let y = c[1];
cells[x][y] = choose(
intersection(
iota(15, 1 + 15 * y),
setminus(this.B, used)), 1)[0];
used.add(cells[x][y]);
}
}
var cs = [];
for (var i = 0; i < 5; ++i) {
if (cells[i][i] == null) cs.push([i, i]);
}
if (cs.length > 0) {
let c = choose(cs, 1)[0];
let x = c[0];
let y = c[1];
cells[x][y] = choose(
intersection(
iota(15, 1 + 15 * y),
setminus(this.B, used)), 1)[0];
used.add(cells[x][y]);
}
var cs = [];
for (var i = 0; i < 5; ++i) {
let j = 4 - i;
if (cells[i][j] == null) cs.push([i, j]);
}
if (cs.length > 0) {
let c = choose(cs, 1)[0];
let x = c[0];
let y = c[1];
cells[x][y] = choose(
intersection(
iota(15, 1 + 15 * y),
setminus(this.B, used)), 1)[0];
used.add(cells[x][y]);
}
// まだ空いてるマスはどんな数字で埋めても良い
for (var i = 0; i < 5; ++i) {
for (var j = 0; j < 5; ++j) {
if (cells[i][j] == null) {
let x = choose(setminus(iota(15, 1 + 15 * j), used), 1)[0];
cells[i][j] = x;
used.add(x);
}
}
}
// print
let read = new Set(this.read());
for (var i = 0; i < 5; ++i) {
console.log(
cells[i]
.map((x) => {
var s = typeof(x) === 'string' ? x
: typeof(x) === 'number' ? (x < 10 ? ` ${x}` : `${x}`)
: '??';
s = (x == 'FR' || read.has(x)) ? `[${s}]` : ` ${s} `;
return s;
})
.join(' '))
}
return cells;
}
}
var bg = new Bingo();
console.log(bg.read().join(' '));
console.log();
bg.sheet();
console.log();
bg.sheet();
console.log();
bg.sheet();
@cympfh
Copy link
Author

cympfh commented Sep 2, 2018

9 25 74 24 1 2 51 19 49 58 44 60 71 18 35 65 42 38 12 37 7 28 48 68 75 54

 11   27   41   50   64
  3   16   36   55   67
[ 9] [25] [FR]  57   70
  4   30   45   46   63
[12] [24] [44] [54] [65]

[ 7]  22   41   53   64
 13  [28]  34   56   67
[ 1]  30  [FR]  47   66
[ 9] [18] [44] [54] [74]
  6   27   31   59  [68]

 13   21  [35] [49]  64
  8   26   45  [60] [68]
[ 7]  23  [FR] [54]  62
 10   29   43  [51]  70
  6   17   39  [58]  67
node test.js  0.10s user 0.01s system 107% cpu 0.100 total

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment