jsx
Version:
a faster, safer, easier JavaScript
418 lines (379 loc) • 10.8 kB
JSX
// Reversi ported from https://bitbucket.org/7shi/ts-reversi
import "js/web.jsx";
class Config {
static const STAGE_WIDTH = 320;
static const STAGE_HEIGHT = 260;
}
class Board {
var canvas : HTMLCanvasElement;
var cx : CanvasRenderingContext2D;
var board : number[][];
var win : number[][];
var player : number;
var black : number;
var white : number;
var message : string;
var ignore = false;
var showWin = false;
var think : () -> void;
function constructor(canvas : HTMLCanvasElement) {
this.init();
if (! canvas) return;
this.canvas = canvas;
this.cx = canvas.getContext("2d") as CanvasRenderingContext2D;
this.draw();
this.think = () -> { this.thinkMonteCarlo(); };
canvas.addEventListener("mousedown",
(e) -> { this.onMouseDown(e as MouseEvent); });
}
function init() : void {
this.board = [
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 2, 1, 0, 0, 0],
[0, 0, 0, 1, 2, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]
];
this.win = [
new number[],
new number[],
new number[],
new number[],
new number[],
new number[],
new number[],
new number[]
];
this.player = 1;
this.message = "";
this.count();
}
function draw() : void {
var cx = this.cx;
cx.fillStyle = "green";
cx.fillRect(0, 0, this.canvas.width, this.canvas.height);
cx.strokeStyle = "black";
for (var i = 0; i <= 8; i++) {
cx.beginPath();
cx.moveTo(i * 30 + 10, 10);
cx.lineTo(i * 30 + 10, 250);
cx.moveTo( 10, i * 30 + 10);
cx.lineTo(250, i * 30 + 10);
cx.stroke();
}
cx.font = "10pt sans-serif";
cx.textAlign = "center";
cx.textBaseline = "middle";
for (var y = 0; y <= 7; y++) {
for (var x = 0; x <= 7; x++) {
this.drawStone(x, y, this.board[y][x]);
if (this.showWin && this.win != null && this.win[y][x] != null) {
cx.fillStyle = "red";
cx.fillText(this.win[y][x] as string,
x * 30 + 25, y * 30 + 25);
}
}
}
this.drawStone(8.3, 6.5, this.player);
cx.fillText("Turn", 275, 245);
this.drawStone(8.3, 0, 1);
cx.fillText(this.black as string, 275, 55);
this.drawStone(8.3, 2, 2);
cx.fillText(this.white as string, 275, 115);
if (this.message != "") {
cx.fillStyle = "white";
cx.strokeStyle = "red";
cx.beginPath();
cx.rect(20, 120, 220, 20);
cx.fill();
cx.stroke();
cx.fillStyle = "black";
cx.fillText(this.message, 120, 130);
}
}
function drawStone(x : number, y : number, c : number) : void {
var cx = this.cx;
if (c == 1) {
cx.fillStyle = "black";
} else if (c == 2) {
cx.fillStyle = "white";
} else {
return;
}
cx.beginPath();
cx.arc(x * 30 + 25, y * 30 + 25, 14, 0, 2 * Math.PI);
cx.fill();
}
function next(x : number, y : number) : void {
if (0 <= x && x <= 7 && 0 <= y && y <= 7) {
this.board[y][x]++;
if (this.board[y][x] == 3) {
this.board[y][x] = 0;
}
}
}
function put(x : number, y : number) : number {
var stone = 0;
stone += this.putDirection(x, y, 1, 0);
stone += this.putDirection(x, y, -1, 0);
stone += this.putDirection(x, y, 0, 1);
stone += this.putDirection(x, y, 0, -1);
stone += this.putDirection(x, y, 1, 1);
stone += this.putDirection(x, y, 1, -1);
stone += this.putDirection(x, y, -1, 1);
stone += this.putDirection(x, y, -1, -1);
if (stone > 0) {
this.board[y][x] = this.player;
stone++;
this.count();
}
return stone;
}
function putDirection(x : number, y : number, dx : number, dy : number) : number {
var stone = this.countDirection(x, y, dx, dy);
for (var i = 1; i <= stone; i++) {
this.board[y + dy * i][x + dx * i] = this.player;
}
return stone;
}
function check(x : number, y : number, n : number) : boolean {
return 0 <= x && x <= 7 && 0 <= y && y <= 7 && this.board[y][x] == n;
}
function change() : number {
this.player = 3 - this.player;
if (this.canPut()) return 1;
this.player = 3 - this.player;
if (this.canPut()) return 2;
if (this.black > this.white) {
this.message = "Black Wins!";
} else if (this.black < this.white) {
this.message = "White Wins!";
} else {
this.message = "Draw!";
}
return 3;
}
function count() : void {
this.black = 0;
this.white = 0;
for (var y = 0; y <= 7; y++) {
for (var x = 0; x <= 7; x++) {
if (this.board[y][x] == 1) {
this.black++;
} else if (this.board[y][x] == 2) {
this.white++;
}
}
}
}
function checkCount(x : number, y : number) : boolean {
return this.countDirection(x, y, 1, 0) > 0 ||
this.countDirection(x, y, -1, 0) > 0 ||
this.countDirection(x, y, 0, 1) > 0 ||
this.countDirection(x, y, 0, -1) > 0 ||
this.countDirection(x, y, 1, 1) > 0 ||
this.countDirection(x, y, 1, -1) > 0 ||
this.countDirection(x, y, -1, 1) > 0 ||
this.countDirection(x, y, -1, -1) > 0;
}
function countDirection(x : number, y : number, dx : number, dy : number) : number {
if (this.check(x, y, 0)) {
var rival = 3 - this.player;
var stone = 0;
var x1 = x + dx;
var y1 = y + dy;
while (this.check(x1, y1, rival)) {
stone++;
x1 += dx;
y1 += dy;
}
if (stone > 0 && this.check(x1, y1, this.player)) {
return stone;
}
}
return 0;
}
function canPut() : boolean {
for (var y = 0; y <= 7; y++) {
for (var x = 0; x <= 7; x++) {
if (this.checkCount(x, y)) {
return true;
}
}
}
return false;
}
function thinkRandom() : number[] {
var x, y;
do {
x = Math.floor(Math.random() * 8);
y = Math.floor(Math.random() * 8);
} while (this.put(x, y) == 0);
return [x, y];
}
function countPut(x: number, y: number) : number {
var stone = 0;
if (this.check(x, y, 0)) {
stone += this.countDirection(x, y, 1, 0);
stone += this.countDirection(x, y, -1, 0);
stone += this.countDirection(x, y, 0, 1);
stone += this.countDirection(x, y, 0, -1);
stone += this.countDirection(x, y, 1, 1);
stone += this.countDirection(x, y, 1, -1);
stone += this.countDirection(x, y, -1, 1);
stone += this.countDirection(x, y, -1, -1);
}
return stone;
}
function thinkMost() : void {
var max = 0, tx = 0, ty = 0;
for (var y = 0; y <= 7; y++) {
for (var x = 0; x <= 7; x++) {
var stone = this.countPut(x, y);
if (max < stone) {
max = stone;
tx = x;
ty = y;
}
}
}
if (max > 0) this.put(tx, ty);
}
function clone() : Board {
var ret = new Board(null);
for (var y = 0; y <= 7; y++) {
for (var x = 0; x <= 7; x++) {
ret.board[y][x] = this.board[y][x];
}
}
ret.player = this.player;
ret.black = this.black;
ret.white = this.white;
return ret;
}
function thinkMonteCarlo() : void {
this.win = [
new number[],
new number[],
new number[],
new number[],
new number[],
new number[],
new number[],
new number[]
];
for (var i = 1; i <= 1000; i++) {
var board = this.clone();
var pt = board.thinkRandom();
var x = pt[0], y = pt[1];
while (board.change() != 3) {
board.thinkRandom();
}
if ((this.player == 1 && board.black > board.white) ||
(this.player == 2 && board.black < board.white)) {
if (this.win[y][x] == null) {
this.win[y][x] = 1;
} else {
this.win[y][x]++;
}
} else if (board.black != board.white) {
if (this.win[y][x] == null) {
this.win[y][x] = -1;
} else {
this.win[y][x]--;
}
}
}
var max = null : Nullable.<number>;
var tx = 0;
var ty = 0;
for (var y = 0; y <= 7; y++) {
for (var x = 0; x <= 7; x++) {
if (this.win[y][x] != null &&
(max == null || max < this.win[y][x])) {
max = this.win[y][x];
tx = x;
ty = y;
}
}
}
if (max != null) {
this.put(tx, ty);
} else {
this.thinkRandom();
}
}
function onMouseDown(e : MouseEvent) : void {
if (this.ignore) return;
if (this.message != "") {
this.init();
this.draw();
return;
}
var r = this.canvas.getBoundingClientRect();
var x = Math.floor((e.clientX - r.left - 10) / 30);
var y = Math.floor((e.clientY - r.top - 10) / 30);
if (this.put(x, y) == 0) return;
this.win = null;
var chg = this.change();
this.draw();
if (chg != 1) return;
this.ignore = true;
dom.window.setTimeout(function start() : void {
this.think();
var chg = this.change();
this.draw();
if (chg == 2) {
start();
}
else {
this.ignore = false;
}
}, 50);
}
function configureFromForm() : void {
var levels = dom.document.getElementsByName('level');
for (var i = 0; i < levels.length; i++) {
var radio = levels[i] as HTMLInputElement;
if (radio.checked) {
switch (radio.value) {
case "random":
this.think = () -> { this.thinkRandom(); };
break;
case "most":
this.think = () -> { this.thinkMost(); };
break;
case "montecarlo":
this.think = () -> { this.thinkMonteCarlo(); };
break;
default:
log "no such algorithm: " + radio.value;
}
}
var showWinningRate = dom.id("show_winning_rate") as HTMLInputElement;
assert showWinningRate != null;
this.showWin = showWinningRate.checked;
this.draw();
}
}
}
class _Main {
static function createCanvas() : HTMLCanvasElement {
var canvas = dom.createElement('canvas') as HTMLCanvasElement;
canvas.width = Config.STAGE_WIDTH;
canvas.height = Config.STAGE_HEIGHT;
dom.id('world').appendChild(canvas);
return canvas;
}
static function main(args : string[]) : void {
var board = new Board(_Main.createCanvas());
board.configureFromForm();
dom.id('configuration').addEventListener('click', (e) -> { board.configureFromForm(); });
}
}
// vim: set expandtab :
// vim: set tabstop=2 :
// vim: set shiftwidth=2 :