Created
September 2, 2018 05:41
-
-
Save cympfh/03829fca99d3327eddb2963b5c2ec51b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} | |
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(); |
Author
cympfh
commented
Sep 2, 2018
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment