jocly/src/core/jocly.core.js
2017-04-28 16:32:25 +02:00

1046 lines
29 KiB
JavaScript

/* Copyright 2017 Jocly
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects
* for all of the code used other than as permitted herein. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version. If you
* delete this exception statement from all source files in the program,
* then also delete it in the license file.
*/
"use strict";
/*
* Keys to understand this code
*
* This script can be executed under various contexts:
* A. in the browser within the application context
* B. in the browser within an iframe controlled by the application
* C. in the browser as a worker for running the AI
* D. in node.js
*
* In case A, the Match object can either be:
* - attached to an element (A1)
* - not attached (A2)
*
* A Match object is only glue. The actual object doing the job is generated
* from function CreateInternalGame(). This object is constructed by defining
* a base (from jocly.game.js) overloaded by the model (<game-name>_model.js),
* and possibly by the view (<game-name>_view.js) if there is a need for a UI
* (case B only).
*
* In cases A2, B, C and D, the Match object contains 'game', the actual smart object
* In case A1, it contains property 'iframe' which is an actual iframe running
* the code as case B. Properties 'game' and 'iframe' should never be set at the
* same time.
*
* Match.attachElement() moves from case A2 to A1, creating an iframe running in
* in case B and passing the responsability of the actual game object to it.
* Match.detachElement() (A1 to A2) gets the game back from the iframe and destroys
* the iframe.
*
* In case B (and only in this case), the Match object has a property 'area'
* representing the DOM element containing the UI.
*
* By convention, Match object method names starts with a lowercase character,
* the game object with uppercase. Both APIs may have some similarities but
* are actually different.
*
* Most Match API methods do the following: check whether we are in case A1,
* if so, the command/reply is passed/answered to/from the iframe running case B.
* In all other cases, the action is performed locally using the API of the game
* object.
*
*/
(function () {
// Check in what context this code is running
var jsContext = "browser";
if (typeof WorkerGlobalScope === 'undefined' && typeof SystemJS === 'undefined')
jsContext = "node";
else if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope)
jsContext = "worker";
var gameClassCache = {};
function CreateGame(gameName, gameDescr, base, model, config) {
var Game = function () {
this.g = {}; // some games assume this member exists
};
Object.assign(Game.prototype, base.Game.prototype, model.model.Game);
var Board = function () { };
Object.assign(Board.prototype, base.Board.prototype, model.model.Board);
Game.prototype.mBoardClass = Board;
var Move = function (args) { this.Init(args); };
Object.assign(Move.prototype, base.Move.prototype, model.model.Move);
Game.prototype.mMoveClass = Move;
Game.prototype.config = config.config;
if (jsContext == "browser") {
Game.prototype.config.baseURL = SystemJS.getConfig().baseURL;
Game.prototype.config.view.fullPath = SystemJS.getConfig().baseURL + "games/" + gameDescr.module;
}
Game.prototype.module = gameDescr.module;
Game.prototype.name = gameName;
var initArgs = {
game: config.config.model.gameOptions,
view: config.config.view
};
gameClassCache[gameName] = {
gameClass: Game,
initArgs: initArgs
}
var game = new Game();
game.Init(initArgs);
return game;
}
function CreateInternalGame(gameName, options) {
var gameClassData = gameClassCache[gameName];
if (gameClassData) {
var promise = new Promise((resolve, reject) => {
var game = new gameClassData.gameClass();
game.Init(gameClassData.initArgs);
resolve(game);
});
return promise;
}
if (jsContext == "worker")
return WorkerCreateGame(gameName, options);
else if (jsContext == "node")
return NodeCreateGame(gameName, options);
else // must be browser
return BrowserCreateGame(gameName, options);
}
function NodeCreateGame(gameName, options) {
var t0 = Date.now();
options = options || {};
var promise = new Promise((resolve, reject) => {
var games = require("./jocly-allgames.js").games;
var gameDescr = games[gameName];
if (!gameDescr)
return reject(new Error("Game " + gameName + " not found"));
var base = require("./jocly.game.js");
var model = require("./games/" + gameDescr.module + "/" + gameName + "-model.js");
var config = require("./games/" + gameDescr.module + "/" + gameName + "-config.js");
var game = CreateGame(gameName, gameDescr, base, model, config);
resolve(game);
});
return promise;
}
function WorkerCreateGame(gameName, options) {
var t0 = Date.now();
options = options || {};
var promise = new Promise((resolve, reject) => {
importScripts("jocly-allgames.js");
var games = exports.games;
var gameDescr = games[gameName];
if (!gameDescr)
return reject(new Error("Game " + gameName + " not found"));
var originalExports = exports;
var base = exports = {};
importScripts("jocly.game.js");
var model = exports = {};
importScripts("games/" + gameDescr.module + "/" + gameName + "-model.js");
var config = exports = {};
importScripts("games/" + gameDescr.module + "/" + gameName + "-config.js");
exports = originalExports;
var game = CreateGame(gameName, gameDescr, base, model, config);
resolve(game);
});
return promise;
}
function BrowserCreateGame(gameName, options) {
options = options || {};
var promise = new Promise((resolve, reject) => {
exports.listGames().then((games) => {
var gameDescr = games[gameName];
if (!gameDescr)
return reject(new Error("Game " + gameName + " not found"));
var gamePromises = [
SystemJS.import("jocly.game.js"),
SystemJS.import("games/" + gameDescr.module + "/" + gameName + "-model.js"),
SystemJS.import("games/" + gameDescr.module + "/" + gameName + "-config.js")
];
Promise.all(gamePromises).then(([base, model, config]) => {
var game = CreateGame(gameName, gameDescr, base, model, config);
resolve(game);
}, (e) => {
reject(e);
});
}, reject);
});
return promise;
}
exports.listGames = function () {
var promise = new Promise((resolve, reject) => {
if (jsContext == "browser" || jsContext == "worker")
SystemJS.import("jocly-allgames.js").then((m) => {
var games = {};
var baseURL = SystemJS.getConfig().baseURL;
for (var gameName in m.games) {
var game = Object.assign({}, m.games[gameName]);
game.thumbnail = baseURL + "games/" + game.module + "/" + game.thumbnail;
games[gameName] = game;
}
resolve(games);
}, (e) => {
console.warn("Could not load Jocly games", e);
reject(e);
});
else // must be node
resolve(require("./jocly-allgames.js").games);
});
return promise;
}
exports.createMatch = function (gameName) {
var promise = new Promise(function (resolve, reject) {
CreateInternalGame(gameName)
.then(function (game) {
var proxy = new GameProxy(gameName, game);
resolve(proxy);
}, function (error) {
reject(error);
});
});
return promise;
}
exports.getGameConfig = function (gameName) {
var promise = new Promise((resolve, reject) => {
exports.listGames().then((games) => {
var gameDescr = games[gameName];
if (!gameDescr)
return reject(new Error("Game " + gameName + " not found"));
if (typeof SystemJS != "undefined")
SystemJS.import("games/" + gameDescr.module + "/" + gameName + "-config.js")
.then((config) => {
config.config.view.fullPath = SystemJS.getConfig().baseURL + "games/" + gameDescr.module;
resolve(config.config);
})
.catch(reject);
else
resolve(require("./games/" + gameDescr.module + "/" + gameName + "-config.js").config);
}, reject);
});
return promise;
}
var messageListenerInstalled = false;
var matches = {};
var msgIdRef = 0;
var messageReplies = {};
var matchIdRef = 0;
function GameProxy(gameName, game) {
this.gameName = gameName;
this.game = game;
this.iframe = null;
this.id = ++matchIdRef;
matches[this.id] = this;
}
function MessageListener(event) {
var message = event.data;
var match = matches[message.joclyEmbeddedGameId];
//console.info("proxy receives message",event.data,!!match);
if (match) {
var replyId = message.replyId;
if (replyId) {
var reply = messageReplies[replyId];
if (reply) {
delete messageReplies[replyId];
if (message.message.type == "error") {
var error = new Error(message.message.error.message, message.message.error.fileName, message.message.error.lineNumber);
reply(error);
} else
reply(null, message.message);
}
} else if (message.message.type == "machine-progress") {
if (match.machineProgressListener)
match.machineProgressListener(message.message.progress);
}
}
}
function PostMessage(match, message, reply) {
var wrapper = {
message: message
}
if (reply) {
var replyId = ++msgIdRef;
wrapper.replyId = replyId;
messageReplies[replyId] = reply;
}
try {
match.iframe.contentWindow.postMessage(wrapper, "*");
} catch (e) {
console.error("Message", message, "could not be posted:", e);
}
}
GameProxy.prototype.attachElement = function (element, options) {
if (jsContext == "node")
return Promise.reject(new Error("attachElement(): not supported in node.js"));
if (!this.game || this.iframe)
return Promise.reject(new Error("attachElement(): match is already attached"));
options = options || {};
var self = this;
var promise = new Promise(function (resolve, reject) {
if (!messageListenerInstalled) {
messageListenerInstalled = true;
window.addEventListener("message", MessageListener);
}
self.element = element;
var iframeUrl = "jocly.embed.html";
var iframe = document.createElement("iframe");
var attrs = {
name: "jocly-embedded-" + self.id,
frameborder: 0,
src: SystemJS.getConfig().baseURL + iframeUrl,
width: "100%",
height: "100%"
}
Object.keys(attrs).forEach((attr) => {
iframe.setAttribute(attr, attrs[attr]);
});
Object.assign(iframe.style, {
position: "absolute",
top: 0,
right: 0,
bottom: 0,
left: 0,
whiteSpace: "normal",
});
var wrapper = document.createElement("div");
Object.assign(wrapper.style, {
position: "relative",
whiteSpace: "nowrap",
width: "100%",
height: "100%"
});
wrapper.appendChild(iframe);
element.appendChild(wrapper);
self.iframe = iframe;
iframe.onload = () => {
PostMessage(self, {
type: "init",
id: self.id,
gameName: self.gameName,
playedMoves: self.game.mPlayedMoves,
options: options
}, (error, reply) => {
if (error)
reject(error);
else {
self.game = null;
resolve();
}
});
};
});
return promise;
}
GameProxy.prototype.detachElement = function () {
if (jsContext == "node")
return Promise.reject(new Error("detachElement(): not supported in node.js"));
if (this.game || !this.iframe)
return Promise.reject(new Error("detachElement(): match is not attached"));
var self = this;
var promise = new Promise(function (resolve, reject) {
Promise.all([
CreateInternalGame(self.gameName),
ProxiedMethod(self, "getPlayedMoves")
])
.then(([game, moves]) => {
self.game = game;
game.Load({
playedMoves: moves
});
return ProxiedMethod(self, "destroy");
})
.then(() => {
while (self.element.firstChild)
self.element.removeChild(self.element.firstChild);
delete self.element;
delete self.iframe;
resolve();
})
.catch(reject);
});
return promise;
}
function ProxiedMethod(match, methodName, args) {
args = Array.from(args || []);
var promise = new Promise(function (resolve, reject) {
PostMessage(match, {
type: "method",
id: match.id,
methodName: methodName,
args: args,
}, (error, reply) => {
if (error)
reject(error);
else
resolve.apply(null, reply && reply.args || []);
});
});
return promise;
}
GameProxy.prototype.getTurn = function () {
if (this.game) {
var self = this;
var promise = new Promise(function (resolve, reject) {
var who = self.game.GetWho();
resolve(who);
});
return promise;
} else
return ProxiedMethod(this, "getTurn", arguments);
}
GameProxy.prototype.getMoveString = function (move, format) {
if (this.game) {
var self = this;
var promise = new Promise(function (resolve, reject) {
if (Array.isArray(move))
resolve(
move.map((m) => {
return self.game.CreateMove(m).ToString(format);
})
)
else
resolve(self.game.CreateMove(move).ToString(format));
});
return promise;
} else
return ProxiedMethod(this, "getMoveString", arguments);
}
GameProxy.prototype.pickMove = function (moveString) {
if (this.game) {
var self = this;
var promise = new Promise(function (resolve, reject) {
if (!self.game.mBoard.mMoves || self.game.mBoard.mMoves.length == 0)
self.game.mBoard.GenerateMoves(self.game);
var move = self.game.GetBestMatchingMove(moveString, self.game.mBoard.mMoves);
resolve(move);
});
return promise;
} else
return ProxiedMethod(this, "pickMove", arguments);
}
GameProxy.prototype.playMove = function (move) {
if (jsContext == "node" || (!this.iframe && !this.area))
return this.applyMove(move);
if (this.game) {
var self = this;
var promise = new Promise(function (resolve, reject) {
self.game.PlayMove(move)
.then(() => {
self.game.InvertWho();
var finished = self.game.GetFinished();
self.game.DisplayBoard();
resolve({
finished: !!finished,
winner: finished
});
})
.catch((err) => {
reject(err);
});
});
return promise;
} else
return ProxiedMethod(this, "playMove", arguments);
}
GameProxy.prototype.applyMove = function (move) {
if (this.game) {
var self = this;
var promise = new Promise(function (resolve, reject) {
self.game.ApplyMove(move);
self.game.InvertWho();
var finished = self.game.GetFinished();
if (self.area)
self.game.DisplayBoard();
resolve({
finished: !!finished,
winner: finished
});
});
return promise;
} else
return ProxiedMethod(this, "applyMove", arguments);
}
GameProxy.prototype.destroy = function (move) {
var self = this;
if (this.game) {
var promise = new Promise(function (resolve, reject) {
if (self.area)
self.game.GameDestroyView();
resolve();
});
return promise;
} else
return this.detachElement()
.then(() => {
delete matches[self.id];
})
}
GameProxy.prototype.getPlayedMoves = function () {
if (this.game) {
var self = this;
var promise = new Promise(function (resolve, reject) {
resolve(Array.from(self.game.mPlayedMoves));
});
return promise;
} else
return ProxiedMethod(this, "getPlayedMoves");
}
GameProxy.prototype.userTurn = function () {
if (jsContext == "node")
return Promise.reject(new Error("userTurn(): not supported in node.js"));
if (!this.area && !this.iframe)
return Promise.reject(new Error("userTurn(): match is not attached to DOM element"));
if (this.game) {
var self = this;
var savedHumanMove;
function Restore() {
self.game.HumanMove = savedHumanMove;
delete self.userTurnReject;
}
var promise = new Promise(function (resolve, reject) {
// ensure match is not already in user input
var promise2;
if (self.userTurnReject)
promise2 = self.abortUserTurn();
else
promise2 = new Promise((resolve) => { resolve() });
promise2.then(() => { }, () => { })
.then(() => {
self.userTurnReject = reject;
function HumanMove(move) {
function Resolve() {
self.game.InvertWho();
var finished = self.game.GetFinished();
self.game.DisplayBoard();
resolve({
move: move,
finished: !!finished,
winner: finished
});
}
if (self.game.config.view.animateSelfMoves === false) {
self.game.ApplyMove(move);
Resolve();
} else
self.game.PlayMove(move)
.then(Resolve, reject);
}
savedHumanMove = self.game.HumanMove;
self.game.HumanMove = HumanMove;
self.game.HumanTurn();
});
});
return promise
.then((result) => {
Restore();
return result;
})
.catch((err) => {
Restore();
throw err;
})
} else
return ProxiedMethod(this, "userTurn");
}
GameProxy.prototype.abortUserTurn = function (failOnNotUserTurn) {
if (jsContext == "node")
return Promise.reject(new Error("abortUserTurn(): not supported in node.js"));
if (!this.area && !this.iframe)
return Promise.reject(new Error("abortUserTurn(): match is not attached to DOM element"));
if (this.game) {
var self = this;
if (!this.userTurnReject)
if (failOnNotUserTurn)
return Promise.reject(new Error("abortUserTurn(): not in user input mode"));
else
return Promise.resolve();
var promise = new Promise(function (resolve, reject) {
self.game.HumanTurnEnd();
var userTurnReject = self.userTurnReject;
delete self.userTurnReject;
userTurnReject(new Error("User input aborted"));
resolve();
});
return promise;
} else
return ProxiedMethod(this, "abortUserTurn", arguments);
}
GameProxy.prototype.machineSearch = function (options) {
var self = this;
if (this.game) {
options = Object.assign({
level: self.game.config.model.levels[0],
threaded: true
}, options);
var savedMachineMove, savedMachineProgress;
function Restore() {
self.game.MachineMove = savedMachineMove;
self.game.MachineProgress = savedMachineProgress;
delete self.machineSearchReject;
}
var promise = new Promise(function (resolve, reject) {
// ensure match is not already in user input
var promise2;
if (self.machineSearchReject)
promise2 = self.abortMachineSearch();
else
promise2 = new Promise((resolve) => { resolve() });
promise2.then(() => { }, () => { })
.then(() => {
self.machineSearchReject = reject;
function MachineMove(result) {
var move = result.move;
delete result.move;
var finished = self.game.GetFinished();
resolve({
move: move,
finished: !!finished,
winner: finished
});
}
savedMachineMove = self.game.MachineMove;
self.game.MachineMove = MachineMove;
function MachineProgress(percent) {
if (self.area)
window.parent.postMessage({
joclyEmbeddedGameId: self.id,
message: {
type: "machine-progress",
progress: percent
}
}, "*");
}
savedMachineProgress = self.game.MachineProgress;
self.game.MachineProgress = MachineProgress;
self.game.StartMachine(options);
});
});
return promise
.then((result) => {
Restore();
return result;
})
.catch((err) => {
Restore();
throw err;
})
} else {
function Restore2() {
delete self.machineProgressListener;
}
options = options || {};
self.machineProgressListener = options.progress || function () { };
delete options.progress;
return ProxiedMethod(this, "machineSearch", [options])
.then((result) => {
Restore2();
return result;
})
.catch((err) => {
Restore2();
throw err;
})
}
}
GameProxy.prototype.abortMachineSearch = function (failOnNotMachineSearch) {
if (this.game) {
var self = this;
if (!this.machineSearchReject)
if (failOnNotMachineSearch)
return Promise.reject(new Error("abortMachineSearch(): machine not searching"));
else
return Promise.resolve();
var promise = new Promise(function (resolve, reject) {
self.game.StopThreadedMachine();
var machineSearchReject = self.machineSearchReject;
delete self.machineSearchReject;
machineSearchReject(new Error("Machine search aborted"));
resolve();
});
return promise;
} else
return ProxiedMethod(this, "abortMachineSearch");
}
GameProxy.prototype.setViewOptions = function (options) {
if (jsContext == "node")
return Promise.reject(new Error("setViewOptions(): not supported in node.js"));
if (!this.area && !this.iframe)
return Promise.reject(new Error("setViewOptions(): match is not attached to DOM element"));
if (this.game) {
var self = this;
var promise = new Promise(function (resolve, reject) {
self.game.GameDestroyView();
const optDefs = {
"mSkin": "skin",
"mNotation": "notation",
"mSounds": "sounds",
"mShowMoves": "showMoves",
"mAutoComplete": "autoComplete",
"mAnaglyph": "anaglyph"
}
for (var o in optDefs)
if (typeof options[optDefs[o]] != "undefined")
self.game[o] = options[optDefs[o]];
if(options.viewAs && self.game.mViewOptions.switchable)
self.game.mViewAs = options.viewAs;
self.game.GameInitView();
self.game.DisplayBoard();
resolve();
});
return promise;
} else
return ProxiedMethod(this, "setViewOptions", arguments);
}
GameProxy.prototype.getViewOptions = function () {
if (this.game) {
var self = this;
var promise = new Promise(function (resolve, reject) {
var options = {
skin: self.game.mSkin,
sounds: self.game.mSounds,
anaglyph: self.game.mAnaglyph
}
if (self.game.mViewOptions.useNotation)
options.notation = !!self.game.mNotation;
if (self.game.mViewOptions.useShowMoves)
options.showMoves = !!self.game.mShowMoves;
if (self.game.mViewOptions.useAutoComplete)
options.autoComplete = !!self.game.mAutoComplete;
if(self.game.mViewOptions.switchable)
options.viewAs = self.game.mViewAs;
resolve(options);
});
return promise;
} else
return ProxiedMethod(this, "getViewOptions");
}
GameProxy.prototype.getFinished = function () {
if (this.game) {
var self = this;
var promise = new Promise(function (resolve, reject) {
var finished = self.game.GetFinished();
resolve({
finished: !!finished,
winner: finished
});
});
return promise;
} else
return ProxiedMethod(this, "getFinished");
}
GameProxy.prototype.rollback = function (index) {
if (this.game) {
var self = this;
var promise = new Promise(function (resolve, reject) {
if (index < 0)
index = self.game.mPlayedMoves.length + index;
index = Math.max(0, Math.min(index, self.game.mPlayedMoves.length));
var playedMoves = self.game.mPlayedMoves;
self.game.BackTo(index, playedMoves);
if (self.area)
self.game.DisplayBoard();
resolve();
});
return promise;
} else
return ProxiedMethod(this, "rollback", arguments);
}
GameProxy.prototype.otherPlayer = function (player) {
var promise = new Promise(function (resolve, reject) {
if (player == 1)
resolve(-1);
else if (player == -1)
resolve(1);
else
reject(new Error("otherPlayer: invalid input"));
});
return promise;
}
GameProxy.prototype.save = function () {
if (this.game) {
var self = this;
var promise = new Promise(function (resolve, reject) {
resolve({
playedMoves: Array.from(self.game.mPlayedMoves),
initialBoard: self.game.mInitialString,
game: self.gameName
})
});
return promise;
} else
return ProxiedMethod(this, "save");
}
GameProxy.prototype.load = function (data) {
if (this.game) {
var self = this;
var promise = new Promise(function (resolve, reject) {
if (data.game && data.game != self.gameName)
return reject(new Error("Trying to load " + data.game + " to " + self.gameName + " match"));
self.game.Load(data);
if (self.area)
self.game.DisplayBoard();
resolve();
});
return promise;
} else
return ProxiedMethod(this, "load", arguments);
}
GameProxy.prototype.getConfig = function () {
if (this.game) {
var self = this;
var promise = new Promise(function (resolve, reject) {
resolve(self.game.config);
});
return promise;
} else
return ProxiedMethod(this, "getConfig");
}
GameProxy.prototype.viewAs = function (player) {
if (jsContext == "node")
return Promise.reject(new Error("viewAs(): not supported in node.js"));
if (!this.area && !this.iframe)
return Promise.reject(new Error("viewAs(): match is not attached to DOM element"));
console.warn("Match.viewAs() is obsolete, use Match.setViewOptions() instead");
if (this.game) {
var self = this;
var promise = new Promise(function (resolve, reject) {
self.game.GameDestroyView();
self.game.mViewAs = player;
self.game.GameInitView();
self.game.DisplayBoard();
resolve();
});
return promise;
} else
return ProxiedMethod(this, "viewAs", arguments);
}
GameProxy.prototype.viewControl = function (command, options) {
if (jsContext == "node")
return Promise.reject(new Error("viewAs(): not supported in node.js"));
if (!this.area && !this.iframe)
return Promise.reject(new Error("viewAs(): match is not attached to DOM element"));
if (this.game) {
var self = this;
return self.game.ViewControl(command, options)
} else
return ProxiedMethod(this, "viewControl", arguments);
}
GameProxy.prototype.getPossibleMoves = function () {
var self = this;
if (this.game) {
var self = this;
return new Promise(function (resolve, reject) {
if (!self.game.mBoard.mMoves || self.game.mBoard.mMoves.length == 0)
self.game.mBoard.GenerateMoves(self.game);
resolve(self.game.mBoard.mMoves);
});
} else
return ProxiedMethod(this, "getPossibleMoves", arguments);
}
GameProxy.prototype.getBoardState = function (format) {
var self = this;
if (this.game) {
var self = this;
return new Promise(function (resolve, reject) {
resolve(self.game.mBoard.ExportBoardState(self.game,format));
});
} else
return ProxiedMethod(this, "getBoardState", arguments);
}
GameProxy.prototype.getInitialBoardState = function (format) {
var self = this;
if (this.game) {
var self = this;
return new Promise(function (resolve, reject) {
resolve(self.game.ExportInitialBoardState(format));
});
} else
return ProxiedMethod(this, "getInitialBoardState", arguments);
}
GameProxy.prototype.resetView = function (force) {
if (jsContext == "node")
return Promise.reject(new Error("resetView(): not supported in node.js"));
if (!this.area && !this.iframe)
return Promise.reject(new Error("resetView(): match is not attached to DOM element"));
if (this.game) {
var self = this;
return self.viewControl("stopAnimations")
.then((shouldRedisplayBoard) => {
if(shouldRedisplayBoard || force)
self.game.DisplayBoard();
})
} else
return ProxiedMethod(this, "resetView");
}
// experimental
GameProxy.prototype.getAvailableSkins = function () {
if (jsContext == "node")
return Promise.reject(new Error("resetView(): not supported in node.js"));
if (this.game) {
var self = this;
var supports3D = ( function () {
try {
return !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'experimental-webgl' );
}
catch( e ) {
return false;
}
} )();
return Promise.resolve(
this.game.mViewOptions.skins.filter((skin)=>{
return supports3D || !skin["3d"];
})
)
} else
return ProxiedMethod(this, "getAvailableSkins");
}
exports._createInternalGame = CreateInternalGame; // do not use this
exports.PLAYER_A = 1;
exports.PLAYER_B = -1;
exports.DRAW = 2;
})();