whisper.cat stuff

This commit is contained in:
[Harper Innes]
2023-10-05 23:28:32 +11:00
parent 2a6e28637c
commit f127b4fea8
5224 changed files with 919361 additions and 0 deletions

View File

@ -0,0 +1,409 @@
var g_startOffset = null;
var g_selectedPiece = null;
var moveNumber = 1;
var g_allMoves = [];
var g_playerWhite = true;
var g_changingFen = false;
var g_analyzing = false;
var g_uiBoard;
var g_cellSize = 45;
function UINewGame() {
moveNumber = 1;
var pgnTextBox = document.getElementById("PgnTextBox"); //
pgnTextBox.value = "";
EnsureAnalysisStopped(); // UI (terminate and destroy worker)
ResetGame(); // shouldn't work
if (InitializeBackgroundEngine()) {
g_backgroundEngine.postMessage("go");
}
g_allMoves = [];
RedrawBoard(); //UI
// if player is black play the first move
if (!g_playerWhite) {
SearchAndRedraw(); //UI
}
}
function EnsureAnalysisStopped() {
if (g_analyzing && g_backgroundEngine != null) {
g_backgroundEngine.terminate();
g_backgroundEngine = null;
}
}
function UIAnalyzeToggle() {
if (InitializeBackgroundEngine()) {
if (!g_analyzing) {
g_backgroundEngine.postMessage("analyze");
} else {
EnsureAnalysisStopped();
}
g_analyzing = !g_analyzing;
document.getElementById("AnalysisToggleLink").innerText = g_analyzing ? "Analysis: On" : "Analysis: Off";
} else {
alert("Your browser must support web workers for analysis - (chrome4, ff4, safari)");
}
}
function UIChangeFEN() {
if (!g_changingFen) {
var fenTextBox = document.getElementById("FenTextBox");
var result = InitializeFromFen(fenTextBox.value);
if (result.length != 0) {
UpdatePVDisplay(result);
return;
} else {
UpdatePVDisplay('');
}
g_allMoves = [];
EnsureAnalysisStopped();
InitializeBackgroundEngine();
g_playerWhite = !!g_toMove;
g_backgroundEngine.postMessage("position " + GetFen());
RedrawBoard();
}
}
function UIChangeStartPlayer() {
g_playerWhite = !g_playerWhite;
RedrawBoard();
}
function UpdatePgnTextBox(move) {
var pgnTextBox = document.getElementById("PgnTextBox");
if (g_toMove != 0) {
pgnTextBox.value += moveNumber + ". ";
moveNumber++;
}
pgnTextBox.value += GetMoveSAN(move) + " ";
}
function UIChangeTimePerMove() {
var timePerMove = document.getElementById("TimePerMove");
g_timeout = parseInt(timePerMove.value, 10);
}
function FinishMove(bestMove, value, timeTaken, ply) {
if (bestMove != null) {
UIPlayMove(bestMove, BuildPVMessage(bestMove, value, timeTaken, ply));
} else {
alert("Checkmate!");
}
}
function UIPlayMove(move, pv) {
UpdatePgnTextBox(move);
g_allMoves[g_allMoves.length] = move;
MakeMove(move);
UpdatePVDisplay(pv);
UpdateFromMove(move);
}
// buggy doesn't update the pgn properly
function UIUndoMove() {
if (g_allMoves.length == 0) {
return;
}
if (g_backgroundEngine != null) {
g_backgroundEngine.terminate();
g_backgroundEngine = null;
}
UnmakeMove(g_allMoves[g_allMoves.length - 1]);
g_allMoves.pop();
if (g_playerWhite !== !!g_toMove && g_allMoves.length !== 0) {
UnmakeMove(g_allMoves[g_allMoves.length - 1]);
g_allMoves.pop();
}
RedrawBoard();
}
function UpdatePVDisplay(pv) {
if (pv != null) {
var outputDiv = document.getElementById("output");
if (outputDiv.firstChild != null) {
outputDiv.removeChild(outputDiv.firstChild);
}
outputDiv.appendChild(document.createTextNode(pv));
}
}
function SearchAndRedraw() {
if (g_analyzing) {
EnsureAnalysisStopped();
InitializeBackgroundEngine();
g_backgroundEngine.postMessage("position " + GetFen());
g_backgroundEngine.postMessage("analyze");
return;
}
if (InitializeBackgroundEngine()) {
g_backgroundEngine.postMessage("search " + g_timeout);
} else {
Search(FinishMove, 99, null); // unasynchronous version fall back
}
}
var g_backgroundEngineValid = true;
var g_backgroundEngine;
function InitializeBackgroundEngine() {
if (!g_backgroundEngineValid) {
return false;
}
if (g_backgroundEngine == null) {
g_backgroundEngineValid = true;
try {
g_backgroundEngine = new Worker("js/garbochess.js");
g_backgroundEngine.onmessage = function (e) {
if (e.data.match("^pv") == "pv") {
UpdatePVDisplay(e.data.substr(3, e.data.length - 3));
} else if (e.data.match("^message") == "message") {
EnsureAnalysisStopped();
UpdatePVDisplay(e.data.substr(8, e.data.length - 8));
} else {
UIPlayMove(GetMoveFromString(e.data), null);
}
}
g_backgroundEngine.error = function (e) {
alert("Error from background worker:" + e.message);
}
g_backgroundEngine.postMessage("position " + GetFen());
} catch (error) {
g_backgroundEngineValid = false;
}
}
return g_backgroundEngineValid;
}
function UpdateFromMove(move) {
var fromX = (move & 0xF) - 4;
var fromY = ((move >> 4) & 0xF) - 2;
var toX = ((move >> 8) & 0xF) - 4;
var toY = ((move >> 12) & 0xF) - 2;
if (!g_playerWhite) {
fromY = 7 - fromY;
toY = 7 - toY;
fromX = 7 - fromX;
toX = 7 - toX;
}
if ((move & moveflagCastleKing) ||
(move & moveflagCastleQueen) ||
(move & moveflagEPC) ||
(move & moveflagPromotion)) {
// more than one piece was moved
// or one piece was modified
// -> entire redraw
RedrawPieces();
} else {
// simply swap piece parents
var fromSquare = g_uiBoard[fromY * 8 + fromX];
$(g_uiBoard[toY * 8 + toX])
.empty()
.append($(fromSquare).children());
}
}
function RedrawPieces() {
for (y = 0; y < 8; ++y) {
for (x = 0; x < 8; ++x) {
var td = g_uiBoard[y * 8 + x];
var pieceY = g_playerWhite ? y : 7 - y;
var piece = g_board[((pieceY + 2) * 0x10) + (g_playerWhite ? x : 7 - x) + 4];
var pieceName = null;
switch (piece & 0x7) {
case piecePawn: pieceName = "pawn"; break;
case pieceKnight: pieceName = "knight"; break;
case pieceBishop: pieceName = "bishop"; break;
case pieceRook: pieceName = "rook"; break;
case pieceQueen: pieceName = "queen"; break;
case pieceKing: pieceName = "king"; break;
}
if (pieceName != null) {
pieceName += "_";
pieceName += (piece & 0x8) ? "white" : "black";
}
if (pieceName != null) {
var img = document.createElement("div");
$(img).addClass('sprite-' + pieceName);
img.style.backgroundImage = "url('img/sprites.png')";
img.width = g_cellSize;
img.height = g_cellSize;
var divimg = document.createElement("div");
divimg.appendChild(img);
$(divimg).draggable({ start: function (e, ui) {
if (g_selectedPiece === null) {
g_selectedPiece = this;
var offset = $(this).closest('table').offset();
g_startOffset = {
left: e.pageX - offset.left,
top: e.pageY - offset.top
};
} else {
return g_selectedPiece == this;
}
}});
$(divimg).mousedown(function(e) {
if (g_selectedPiece === null) {
var offset = $(this).closest('table').offset();
g_startOffset = {
left: e.pageX - offset.left,
top: e.pageY - offset.top
};
e.stopPropagation();
g_selectedPiece = this;
g_selectedPiece.style.backgroundImage = "url('img/transpBlue50.png')";
} else if (g_selectedPiece === this) {
g_selectedPiece.style.backgroundImage = null;
g_selectedPiece = null;
}
});
$(td).empty().append(divimg);
} else {
$(td).empty();
}
}
}
}
function RedrawBoard() {
var div = $("#board")[0];
var table = document.createElement("table");
table.cellPadding = "0px";
table.cellSpacing = "0px";
$(table).addClass('no-highlight');
var tbody = document.createElement("tbody");
g_uiBoard = [];
var dropPiece = function (e, ui) {
// retrive start -> end move
var endX = e.pageX - $(table).offset().left;
var endY = e.pageY - $(table).offset().top;
endX = Math.floor(endX / g_cellSize);
endY = Math.floor(endY / g_cellSize);
var startX = Math.floor(g_startOffset.left / g_cellSize);
var startY = Math.floor(g_startOffset.top / g_cellSize);
// convert coordinates to white-space
if (!g_playerWhite) {
startY = 7 - startY;
endY = 7 - endY;
startX = 7 - startX;
endX = 7 - endX;
}
// gather all possible valid moves
var moves = GenerateValidMoves();
var move = null;
// check if the move is valid
for (var i = 0; i < moves.length; i++) {
if ((moves[i] & 0xFF) == MakeSquare(startY, startX) &&
((moves[i] >> 8) & 0xFF) == MakeSquare(endY, endX)) {
move = moves[i];
}
}
// convert coordinates back to black-space
if (!g_playerWhite) {
startY = 7 - startY;
endY = 7 - endY;
startX = 7 - startX;
endX = 7 - endX;
}
// put the html object back to its original position
// whether the move is valid or not
g_selectedPiece.style.left = 0;
g_selectedPiece.style.top = 0;
if (!(startX == endX && startY == endY) && move != null) {
// if the move is valid
// we add the move to the png texbox
UpdatePgnTextBox(move);
// we save the move in our worker
if (InitializeBackgroundEngine()) {
g_backgroundEngine.postMessage(FormatMove(move));
}
// we add the move to the move list
g_allMoves[g_allMoves.length] = move;
// we apply the move
MakeMove(move);
UpdateFromMove(move);
// update the fen text box
var fen = GetFen();
document.getElementById("FenTextBox").value = fen;
setTimeout("SearchAndRedraw()", 0);
}
// whether the move is valid or not, we remove highlight and clear selection
g_selectedPiece.style.backgroundImage = null;
g_selectedPiece = null;
};
for (y = 0; y < 8; ++y) {
var tr = document.createElement("tr");
for (x = 0; x < 8; ++x) {
var td = document.createElement("td");
td.style.width = g_cellSize + "px";
td.style.height = g_cellSize + "px";
td.style.backgroundColor = ((y ^ x) & 1) ? "#D18947" : "#FFCE9E";
tr.appendChild(td);
g_uiBoard[y * 8 + x] = td;
}
tbody.appendChild(tr);
}
table.appendChild(tbody);
$('body').droppable({ drop: dropPiece });
$(table).mousedown(function(e) {
if (g_selectedPiece !== null) {
dropPiece(e);
}
});
RedrawPieces();
$(div).empty();
div.appendChild(table);
g_changingFen = true;
document.getElementById("FenTextBox").value = GetFen();
g_changingFen = false;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,116 @@
// ECMAScript 5 strict mode
/* jshint globalstrict: true*/
/* global THREE,BOARD_SIZE, COLS,ROWS*/
"use strict";
(function () {
var a = "a".charCodeAt(0);
function Cell() {
this.position = null;
this.index = null;
this.x = null;
this.y = null;
var coordinates = null;
if( arguments.length === 1) {
if (typeof(arguments[0]) === "string" && arguments[0].match(/[a-h][1-8]/) ) {
// position like "a1", "b4", "e7"
this.position = arguments[0];
coordinates = getCoordinatesFromPosition(
this.position
);
this.x = coordinates.x;
this.y = coordinates.y;
this.index = this.x + this.y * COLS;
} else if (arguments[0] >= 0 && arguments[0] < ROWS*COLS) {
// array index
this.index = arguments[0];
coordinates = getCoordinatesFromIndex(
this.index
);
this.x = coordinates.x;
this.y = coordinates.y;
this.position = getPositionFromCoordinates(
this.x,this.y
);
}
} else if( arguments.length === 2 &&
isValid(arguments[0],arguments[1]) ) {
// x and y position (0-based
this.x = arguments[0];
this.y = arguments[1];
this.index = this.x + this.y * COLS;
this.position = getPositionFromCoordinates(
this.x,this.y
);
} else {
throw arguments[0];
}
}
Cell.prototype.toString = function() {
return this.position;
};
Cell.prototype.equals = function () {
if(arguments.length === 1) {
var cell = arguments[0];
if(cell instanceof Cell) {
// it's a Cell object
return cell.position === this.position;
} else {
// it's a string position
return cell === this.position;
}
} else if (arguments.length === 2) {
// it's x,y coordinates
return this.x === arguments[0] &&
this.y === arguments[1];
}
};
Cell.prototype.getWorldPosition = function() {
var cs = BOARD_SIZE / ROWS;
var middle = (BOARD_SIZE-cs)/2;
return new THREE.Vector3(
this.x * cs - middle,
0,
(this.y * cs - middle)
);
};
// private
function getPositionFromCoordinates(x,y) {
return String.fromCharCode(x+a)+(7-y+1);
}
function getCoordinatesFromPosition(position) {
return {
x: position.charCodeAt(0) - a,
y: 7-(parseInt(position.charAt(1),10)-1)
};
}
function getCoordinatesFromIndex(index) {
return {
x: index%COLS,
y: Math.floor(index/COLS) // have to flip y since 3D starts from bottom left
};
}
function isValid() {
if( arguments.length == 2) {
var x = arguments[0];
var y = arguments[1];
return x >= 0 && x < COLS &&
y >= 0 && y < ROWS;
}
return false;
}
window.Cell = Cell;
})();

View File

@ -0,0 +1,596 @@
// ECMAScript 5 strict mode
/* jshint globalstrict: true*/
/* jslint newcap: true */
/* global THREE, $, document, window, console */
/* global LOADING_BAR_SCALE,ROWS,COLS,PIECE_SIZE, BOARD_SIZE, FLOOR_SIZE, WIREFRAME, DEBUG, Cell, WHITE, BLACK, FEEDBACK, SHADOW */
/* global textures, geometries, removeLoader */
/* global initGUI, initInfo, addToPGN, displayCheck, newGame */
/* global initPieceFactory,initCellFactory,createCell,createPiece,createChessBoard, createFloor, createValidCellMaterial,createSelectedMaterial, validCellMaterial, selectedMaterial */
/*global Search,FormatSquare,GenerateMove,MakeMove,GetMoveSAN,MakeSquare,UnmakeMove, FormatMove, ResetGame, GetFen, GetMoveFromString, alert, InitializeFromFen, GenerateValidMoves */
/*global g_inCheck,g_board,g_pieceList, g_toMove, g_timeout:true,g_maxply:true */
/*global moveflagCastleKing, moveflagCastleQueen, moveflagEPC, moveflagPromotion, colorWhite*/
/*global moveflagPromoteQueen,moveflagPromoteRook,moveflagPromoteBishop,moveflagPromoteKnight*/
/*global piecePawn, pieceKnight, pieceBishop, pieceRook, pieceQueen, pieceKing */
"use strict";
var camera;
// list of valid move after each move
// used mostly for cell highlighting
var validMoves = null;
// chess game variables
var g_allMoves = [];
// default promotion
var promotion = moveflagPromoteQueen;
var g_playerWhite = false;
var g_backgroundEngine;
// settings for AI level
var levels = [
{timeout:0,maxply:1},
{timeout:12,maxply:20},
{timeout:25,maxply:40},
{timeout:50,maxply:60},
{timeout:100,maxply:80},
{timeout:200,maxply:100},
{timeout:400,maxply:120},
{timeout:800,maxply:140},
{timeout:1600,maxply:160},
{timeout:3200,maxply:180}
];
(function() {
// general setup
var scene, renderer;
var cameraControls, effectController;
// for picking
var projector;
// 3D board representation
var chessBoard;
// for proper timing
var clock = new THREE.Clock();
var g_backgroundEngineValid = true;
// array for picking
var board3D = [];
// hold current selection
var selectedPiece = null;
var selectedCell = null;
// default values for AI level
g_timeout = 1600;
g_maxply = 49;
/*
* BASIC SETUP
*/
function init() {
// initialize everything for 3D
// CANVAS PARAMETERS
var canvasWidth = window.innerWidth;
var canvasHeight = window.innerHeight;
var canvasRatio = canvasWidth / canvasHeight;
// RENDERER
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.gammaInput = true;
renderer.gammaOutput = true;
renderer.setSize(canvasWidth, canvasHeight);
if ( SHADOW ) {
renderer.shadowMapEnabled = true;
renderer.shadowMapType = THREE.PCFSoftShadowMap;
renderer.shadowMapCascade = true;
}
// black background
renderer.setClearColor( 0x000000, 1.0 );
document.body.appendChild( renderer.domElement );
// CAMERA
camera = new THREE.PerspectiveCamera( 45, canvasRatio, 1, 40000 );
// CONTROLS
cameraControls = new THREE.OrbitAndPanControls(camera, renderer.domElement);
// limitations
cameraControls.minPolarAngle = 0;
cameraControls.maxPolarAngle = 80 * Math.PI/180;
cameraControls.minDistance = 10;
cameraControls.maxDistance = 200;
cameraControls.userZoomSpeed = 1.0;
// default position behind white
// (might want to change that according to color selection)
camera.position.set( 0, 100, 100 );
// LIGHTING
var spotlight = new THREE.SpotLight( 0xFFFFFF, 1.0);
spotlight.position.set( 0, 300, 0 );
spotlight.angle = Math.PI / 2;
spotlight.exponent = 50.0;
spotlight.target.position.set( 0, 0, 0 );
if ( SHADOW ) {
spotlight.castShadow = true;
spotlight.shadowDarkness = 0.5;
//spotlight.shadowMapWidth = 4096; // yeah crazy testing
//spotlight.shadowMapHeight = 4096;
spotlight.shadowBias = -0.001;
}
var whiteLight = new THREE.PointLight( 0xFFEEDD, 0.2);
whiteLight.position.set(0,0,100);
var blackLight = new THREE.PointLight( 0xFFEEDD, 0.2);
blackLight.position.set(0,0,-100);
// generate createPiece and createCell functions
initPieceFactory();
initCellFactory();
// we let chessBoard in global scope to use it for picking
chessBoard = createChessBoard(BOARD_SIZE);
var floor = createFloor(FLOOR_SIZE,BOARD_SIZE);
//floor.position.y = -5*BOARD_SIZE/100;
floor.position.y = chessBoard.height;
// create and fill the scene with default stuff
scene = new THREE.Scene();
scene.add(floor);
scene.add(spotlight);
scene.add(whiteLight);
scene.add(blackLight);
scene.add(chessBoard);
// to make everything black in the background
scene.fog = new THREE.FogExp2( 0x000000, 0.001 );
// little reddish to fake a bit of bounce lighting
scene.add(new THREE.AmbientLight(0x330000));
// for picking
projector = new THREE.Projector();
// Menu
initGUI();
// Check feedback
initInfo();
createValidCellMaterial();
createSelectedMaterial();
// picking event
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
// avoid stretching
window.addEventListener('resize',onResize,false);
}
function onResize() {
var canvas = renderer.domElement;
var w = window.innerWidth;
var h = window.innerHeight;
renderer.setSize(w,h);
// have to change the projection
// else the image will be stretched
camera.aspect = w/h;
camera.updateProjectionMatrix();
}
function animate() {
window.requestAnimationFrame(animate);
render();
}
function render() {
var delta = clock.getDelta();
cameraControls.update(delta);
renderer.render(scene, camera);
}
function UIPlayMove(move,silent) {
// we play the move here by
// adding it to the png list (for display)
addToPGN(move);
// and to the move list (for undos)
g_allMoves[g_allMoves.length] = move;
// committing the move
MakeMove(move);
// redrawing
// silent flag is used when simulating moves for loading PGN
if(!silent) {
redrawBoard();
}
}
function playMove(piece,cell) {
if (piece.cell === undefined || cell.name === undefined) {
return false;
}
// get the positions
var start = new Cell(piece.cell);
var end = new Cell(cell.name);
var startSquare = MakeSquare(start.y, start.x);
var endSquare = MakeSquare(end.y, end.x);
var move = null;
var testPromotion = false;
var p = g_board[startSquare];
if ( ((p & 0x7) === piecePawn) &&
(((start.y === 1) && g_playerWhite) ||
( (start.y === 6) && !g_playerWhite)) &&
(((p & 0x8) && g_playerWhite) ||
(!(p & 0x8) && !g_playerWhite))
) {
testPromotion = true;
}
// check if the move is valid
// validMoves is global and reevaluated after each move
for (var i = 0; i < validMoves.length; i++) {
if (testPromotion) {
// for promotion we one valid move per promotion type
// so we have to be more specific and create the entire move
// with its flag go get it back from validMoves.
// else it's alway a Rook promotion (flag 0x00).
if(validMoves[i] === GenerateMove(startSquare, endSquare, moveflagPromotion | promotion)) {
move = validMoves[i];
break;
}
} else {
// just checking start and end square allows to cover
// all other special moves like "en passant" capture and
// castling
if ( (validMoves[i] & 0xFF) == startSquare &&
((validMoves[i] >> 8) & 0xFF) == endSquare ) {
move = validMoves[i];
break;
}
}
}
if (!(start.x === end.x && start.y === end.y) && move !== null) {
// we send the move to our worker
if (InitializeBackgroundEngine()) {
g_backgroundEngine.postMessage(FormatMove(move));
}
// we play the actual move
UIPlayMove(move,false);
// make the engine play (setTimeOut is used probably to wait for the last postMessage to kick in)
// maybe creating a callback from the worker would be better (more reliable)
setTimeout(SearchAndRedraw, 0);
return true;
}
return false;
}
/*
* AI CONTROL
*/
function EnsureAnalysisStopped() {
if (g_backgroundEngine) {
g_backgroundEngine.terminate();
g_backgroundEngine = null;
}
}
function SearchAndRedraw() {
// the AI is triggered here
if (InitializeBackgroundEngine()) {
g_backgroundEngine.postMessage("search " + g_timeout + "," + g_maxply);
} else {
Search(FinishMove, g_maxply, null); // unasynchronous version fall back
}
}
function FinishMove(bestMove, value, timeTaken, ply) {
// used by the fallback Search
if (bestMove !== null) {
UIPlayMove(bestMove,false);
}
}
function InitializeBackgroundEngine() {
// we initialize the web worker here
if (!g_backgroundEngineValid) {
return false;
}
if (!g_backgroundEngine) {
g_backgroundEngineValid = true;
try {
g_backgroundEngine = new Worker("js/AI/garbochess.js");
g_backgroundEngine.onmessage = function (e) {
if (e.data.match("^pv") == "pv") {
// legacy
} else if (e.data.match("^message") == "message") {
// legacy
EnsureAnalysisStopped();
} else if (e.data.match("^console: ") == "console: ") {
// debugging
console.log(e.data.substr(9));
} else {
// we receive the move from the AI, we play it
UIPlayMove(GetMoveFromString(e.data), false);
}
};
g_backgroundEngine.error = function (e) {
alert("Error from background worker:" + e.message);
};
// set up the current board position
g_backgroundEngine.postMessage("position " + GetFen());
} catch (error) {
g_backgroundEngineValid = false;
}
}
// return false for fallback
return g_backgroundEngineValid;
}
/*
* BOARD
*/
function updateBoard3D() {
// list all the pieces
board3D = [];
for (var y = 0; y < ROWS; y++) {
for (var x = 0; x < COLS; x++) {
var piece = g_board[MakeSquare(y,x)];
var pieceColor = (piece & colorWhite) ? WHITE : BLACK;
var pieceName = null;
switch (piece & 0x7) {
case piecePawn:
pieceName = "pawn";
break;
case pieceKnight:
pieceName = "knight";
break;
case pieceBishop:
pieceName = "bishop";
break;
case pieceRook:
pieceName = "rook";
break;
case pieceQueen:
pieceName = "queen";
break;
case pieceKing:
pieceName = "king";
break;
}
if (pieceName !== null) {
board3D[x+y*COLS] = createPiece(pieceName,pieceColor);
}
}
}
}
function clearBoard() {
// remove all pieces from the board
var cell;
board3D.forEach(function(piece) {
scene.remove(piece);
cell = new Cell(piece.cell);
});
}
function fillBoard() {
// place all the pieces on the board
var cell;
board3D.forEach(function(piece,index) {
cell = new Cell(index);
piece.position = cell.getWorldPosition();
piece.cell = index;
scene.add(piece);
});
}
function redrawBoard() {
validMoves = GenerateValidMoves();
clearBoard();
updateBoard3D();
fillBoard();
displayCheck();
}
/*
* PICKING
*/
function pickPiece(raycaster) {
var intersect = null;
var picked = null;
// intersect piece
var hitList = [];
var hit,piece;
for (var i in board3D) {
if ({}.hasOwnProperty.call(board3D, i)) {
piece = board3D[i];
intersect = raycaster.intersectObject( piece.children[0], true );
if (intersect.length > 0) {
hit = intersect[0];
if (( g_playerWhite && hit.object.parent.color === WHITE ) ||
(!g_playerWhite && hit.object.parent.color === BLACK ) ){
// only pick the right color
hitList.push(hit);
}
}
}
}
// find the closest
hitList.forEach(function(hit) {
if (picked === null || picked.distance > hit.distance) {
picked = hit;
}
});
if (picked) {
return picked.object.parent;
} else {
return null;
}
}
function pickCell(raycaster) {
// here we don't need to test the distance since you can't really
// intersect more than one cell at a time.
var intersect = raycaster.intersectObject( chessBoard, true );
if (intersect.length > 0) {
var pickedCell = intersect[0].object;
return pickedCell;
}
return null;
}
function getRay(event) {
// get the raycaster object from the mouse position
var zoomLevel = window.devicePixelRatio | 1.0 ;
var canvas = renderer.domElement;
var canvasPosition = canvas.getBoundingClientRect();
var mouseX = event.clientX*zoomLevel - canvasPosition.left;
var mouseY = event.clientY*zoomLevel - canvasPosition.top;
var mouseVector = new THREE.Vector3(
2 * ( mouseX / canvas.width ) - 1,
1 - 2 * ( mouseY / canvas.height ));
return projector.pickingRay( mouseVector.clone(), camera );
}
function onDocumentMouseMove( event ) {
var canvas = renderer.domElement;
var raycaster = getRay(event);
var pickedPiece = pickPiece(raycaster);
var pickedCell = pickCell(raycaster);
canvas.style.cursor = "default";
// we are over one of our piece -> hand
if (pickedPiece !== null) {
canvas.style.cursor = "pointer";
}
// if a cell is selected, we unselect it by default
if (selectedCell !== null) {
selectedCell.material = selectedCell.baseMaterial;
}
// if a piece is selected and a cell is picked
if(selectedPiece !== null && pickedCell !== null) {
var start = new Cell(selectedPiece.cell);
var end = new Cell(pickedCell.name);
var move = null;
// we check if it would be a valid move
for (var i = 0; i < validMoves.length; i++) {
if ( (validMoves[i] & 0xFF) == MakeSquare(start.y, start.x) &&
((validMoves[i] >> 8) & 0xFF) == MakeSquare(end.y, end.x)
) {
move = validMoves[i];
break;
}
}
// then if a piece was clicked and we are on a valide cell
// we highlight it and display a hand cursor
if (pickedCell !== null && move !==null) {
selectedCell = pickedCell;
selectedCell.baseMaterial = selectedCell.material;
selectedCell.material = validCellMaterial[selectedCell.color];
canvas.style.cursor = "pointer";
}
}
}
function onDocumentMouseDown( event ) {
var canvas = renderer.domElement;
var raycaster = getRay(event);
var pickedPiece = pickPiece(raycaster);
var pickedCell = pickCell(raycaster);
if (selectedPiece !== null && pickedCell !== null) {
if(playMove(selectedPiece,pickedCell)) {
// a move is played, we reset everything
// any selectedPiece will disappear
// since we redraw everything
selectedPiece = null;
pickedPiece = null;
pickedCell = null;
}
}
// when a click happen, any selected piece gets unselected
if (selectedPiece !== null) {
selectedPiece.children[0].material = selectedPiece.baseMaterial;
//selectedPiece.children[1].material = selectedPiece.baseMaterial;
}
// then if a piece was clicked, we select it
selectedPiece = pickedPiece;
if (selectedPiece !== null) {
selectedPiece.baseMaterial = selectedPiece.children[0].material;
selectedPiece.children[0].material = selectedMaterial[selectedPiece.color];
//selectedPiece.children[1].material = selectedMaterial[selectedPiece.color];
}
}
// all resources (meshs and textures) are loaded
function onLoaded () {
//bar.container.style.display = "none";
removeLoader();
init();
if (DEBUG) {
window.scene = scene;
window.renderer = renderer;
}
newGame(WHITE);
animate();
//setTimeout(loadFEN('8/Q5P1/8/8/8/8/8/2K1k3 w - -'),2000);
}
window.SearchAndRedraw = SearchAndRedraw;
window.onLoaded = onLoaded;
window.redrawBoard = redrawBoard;
window.EnsureAnalysisStopped = EnsureAnalysisStopped;
window.InitializeBackgroundEngine = InitializeBackgroundEngine;
window.UIPlayMove = UIPlayMove;
})();

View File

@ -0,0 +1,420 @@
// ECMAScript 5 strict mode
/* jshint globalstrict: true*/
/* global THREE, $, document, window, console */
/* global LOADING_BAR_SCALE,ROWS,COLS,PIECE_SIZE, BOARD_SIZE, FLOOR_SIZE, WIREFRAME, DEBUG, Cell, WHITE, BLACK, FEEDBACK, SHADOW */
/* global createCell */
/*
* initPieceFactory and initCellFactory need to be called after
* all ressources are loaded (geometry and texture)
*
* they will create the createPiece and createCell function
* and keep some texture/material objects in a closure to avoid
* unnecessary cloning
*/
"use strict";
var geometries = {};
var textures = {};
function initPieceFactory () {
// common textures
var tiling = 4;
var colors = [];
for(var c = 0; c<2; c++) {
colors[c] = textures['texture/wood-'+c+'.jpg'].clone();
colors[c].tile(tiling);
}
var norm = textures['texture/wood_N.jpg'].clone();
norm.tile(tiling);
var spec = textures['texture/wood_S.jpg'].clone();
spec.tile(tiling);
function createPiece(name,color) {
var size = BOARD_SIZE/COLS * PIECE_SIZE;
// container for the piece and its reflexion
var piece = new THREE.Object3D();
// base material for all the piece (only lightmap changes)
var material = new THREE.MeshPhongMaterial({
color:0xffffff,
specular:0xaaaaaa,
shininess:60.0,
map:colors[color],
normalMap:norm,
specularMap:spec,
wireframe:WIREFRAME
});
material.normalScale.set(0.3,0.3);
// urls of geometry and lightmap
var urlJson = '3D/json/'+name+'.json';
var urlAO = 'texture/'+name+'-ao.jpg';
var geo = geometries[urlJson];
// no need to clone this texture
// since its pretty specific
var light = textures[urlAO];
light.format = THREE.LuminanceFormat;
material.lightMap = light;
var mesh = new THREE.Mesh(geo,material);
if (SHADOW) {
mesh.castShadow = true;
mesh.receiveShadow = true;
}
mesh.scale.set(size,size,size);
// we rotate pieces so they face each other (mostly relevant for knight)
mesh.rotation.y += (color == WHITE) ? -Math.PI/2 : Math.PI/2;
// we create the reflection
// it's a cloned with a negative scale on the Y axis
var reflexion = mesh.clone();
reflexion.scale.y *= -1;
reflexion.material = reflexion.material.clone();
reflexion.material.side = THREE.BackSide;
piece.add(mesh);
piece.add(reflexion);
piece.name = name;
piece.color = color;
return piece;
}
// make it global
window.createPiece = createPiece;
}
function initCellFactory() {
var materials = [];
var tiling = 2;
// common textures
var diff;
var norm = textures['texture/wood_N.jpg'].clone();
norm.tile(tiling);
var spec = textures['texture/wood_S.jpg'].clone();
spec.tile(tiling);
for(var c = 0; c<2; c++) {
diff = textures['texture/wood-'+c+'.jpg'].clone();
diff.tile(tiling);
//common material
materials[c] = new THREE.MeshPhongMaterial({
color:0xffffff,
specular:[0xAAAAAA,0x444444][c],
shininess:30.0,
wireframe:WIREFRAME,
transparent:true,
map:diff,
specularMap:spec,
normalMap:norm,
//blending: THREE.AdditiveBlending,
opacity:0.5
});
//materials[c].normalScale.set(0.5,0.5);
}
function createCell(size,color) {
// container for the cell and its reflexion
var geo = new THREE.PlaneGeometry(size,size);
// randomize uv offset to ad a bit of variety
var randU = Math.random();
var randV = Math.random();
var uvs = geo.faceVertexUvs[0][0];
for (var j = 0; j < uvs.length; j++) {
uvs[j].x += randU;
uvs[j].y += randV;
}
var cell = new THREE.Mesh(geo,materials[color]);
if (SHADOW) {
cell.receiveShadow = true;
}
// by default PlaneGeometry is vertical
cell.rotation.x = -Math.PI/2;
cell.color = color;
return cell;
}
// make it global
window.createCell = createCell;
}
function createChessBoard(size) {
// contains everything that makes the board
var lChessBoard = new THREE.Object3D();
var cellSize = size/COLS;
var square,cell;
for(var i=0; i< ROWS*COLS; i++) {
var col = i%COLS;
var row = Math.floor(i/COLS);
cell = new Cell(i);
square = createCell(cellSize,1-(i+row)%2);
square.position = cell.getWorldPosition();
square.name = cell.position;
lChessBoard.add(square);
}
// some fake inner environment color for reflexion
var innerBoard = new THREE.Mesh (
geometries['3D/json/innerBoard.json'],
new THREE.MeshBasicMaterial({
color:0x783e12
})
);
innerBoard.scale.set(size,size,size);
/// board borders
var tiling = 6;
var wood = textures['texture/wood-0.jpg'].clone();
var spec = textures['texture/wood_S.jpg'].clone();
var norm = textures['texture/wood_N.jpg'].clone();
wood.tile(tiling);
spec.tile(tiling);
norm.tile(tiling);
var geo = geometries['3D/json/board.json'];
geo.computeBoundingBox();
var board = new THREE.Mesh (
geo,
new THREE.MeshPhongMaterial({
color:0xffffff,
map:wood,
specular: 0xffffff,
specularMap: spec,
normalMap: norm,
shininess: 60,
normalScale: new THREE.Vector2(0.2,0.2)
})
);
var hCorrection = 0.62; // yeah I should just create a better geometry
board.scale.set(size,size*hCorrection,size);
lChessBoard.height = geo.boundingBox.min.y * board.scale.y;
if (SHADOW) {
board.receiveShadow = true;
board.castShadow = true;
}
lChessBoard.add(innerBoard);
lChessBoard.add(board);
lChessBoard.name = "chessboard";
return lChessBoard;
}
function createFloor(size,chessboardSize) {
// The floor is a fake plane with a hole in it to allow
// for the fake reflexion trick to work
// so we build it vertices by vertices
// material
var tiling = 30*size/1000;
var material = new THREE.MeshPhongMaterial({
color:0xffffff,
wireframe:WIREFRAME ,
specular:0xaaaaaa,
shininess:30
});
var diff = textures['texture/floor.jpg'];
var spec = textures['texture/floor_S.jpg'];
var norm = textures['texture/floor_N.jpg'];
var light = textures['texture/fakeShadow.jpg'];
diff.tile(tiling);
spec.tile(tiling);
norm.tile(tiling);
light.format = THREE.RGBFormat;
material.map = diff;
material.normalMap = norm;
material.normalScale.set(0.6,0.6);
material.specularMap = spec;
material.lightMap = light;
// geometry
var halfBoard = chessboardSize/2;
var halfSize = size/2;
var floorGeo = new THREE.Geometry();
// outter vertices
floorGeo.vertices.push(new THREE.Vector3(-halfSize,0,-halfSize));
floorGeo.vertices.push(new THREE.Vector3( halfSize,0,-halfSize));
floorGeo.vertices.push(new THREE.Vector3( halfSize,0, halfSize));
floorGeo.vertices.push(new THREE.Vector3(-halfSize,0, halfSize));
// hole vertices
floorGeo.vertices.push(new THREE.Vector3(-halfBoard,0,-halfBoard));
floorGeo.vertices.push(new THREE.Vector3( halfBoard,0,-halfBoard));
floorGeo.vertices.push(new THREE.Vector3( halfBoard,0, halfBoard));
floorGeo.vertices.push(new THREE.Vector3(-halfBoard,0, halfBoard));
floorGeo.faceVertexUvs[ 0 ] = [];
floorGeo.faceVertexUvs[ 1 ] = [];
/*
* vertices uvs-lightmap
* 0-----------1 80-----------80
* |\ /| |\ /|
* | \ / | | \ / |
* | \ / | | \ / |
* | 4---5 | | 0---0 |
* | | | | | | | |
* | 7---6 | | 0---0 |
* | / \ | | / \ |
* | / \ | | / \ |
* |/ \| |/ \|
* 3-----------2 80-----------80
*/
// all normals just points upward
var normal = new THREE.Vector3( 0, 1, 0 );
// list of vertex index for each face
var faces = [
[0,4,5,1],
[1,5,6,2],
[2,6,7,3],
[3,7,4,0]
];
faces.forEach( function(f) {
var uvs1 = [];
var uvs2 = [];
var lightU,lightV;
f.forEach(function(v,i) {
// we linearily transform positions
// from a -halfSize-halfSize space
// to a 0-1 space
uvs1.push(new THREE.Vector2(
(floorGeo.vertices[v].x+halfSize)/size,
(floorGeo.vertices[v].z+halfSize)/size
));
lightU = (v < 4) ? 80 : 0;
lightV = (i < 2) ? 0 : 1;
uvs2.push(new THREE.Vector2(lightU,lightV));
});
// we create a new face folowing the faces list
var face = new THREE.Face4(
f[0],f[1],f[2],f[3]
);
// and apply normals (without this, no proper lighting)
face.normal.copy( normal );
face.vertexNormals.push(
normal.clone(),
normal.clone(),
normal.clone(),
normal.clone()
);
// add the face to the geometry's faces list
floorGeo.faces.push(face);
// add uv coordinates to uv channels.
floorGeo.faceVertexUvs[ 0 ].push(uvs1); // for diffuse/normal
floorGeo.faceVertexUvs[ 1 ].push(uvs2); // for lightmap
});
// not sure it's needed but since it's in THREE.PlaneGeometry...
floorGeo.computeCentroids();
var floor = new THREE.Mesh(floorGeo,material);
if(SHADOW) {
floor.receiveShadow = true;
}
floor.name = "floor";
return floor;
}
// special highlighting materials
var validCellMaterial = null;
function createValidCellMaterial () {
validCellMaterial = [];
var tiling = 2;
// common textures
var diff;
var norm = textures['texture/wood_N.jpg'].clone();
norm.tile(tiling);
var spec = textures['texture/wood_S.jpg'].clone();
spec.tile(tiling);
for(var c = 0; c<2; c++) {
diff = textures['texture/wood-1.jpg'].clone();
diff.tile(tiling);
//common material
validCellMaterial[c] = new THREE.MeshPhongMaterial({
color:0x00ff00,
specular:0x999999,
shininess:60.0,
wireframe:WIREFRAME,
map:diff,
specularMap:spec,
normalMap:norm
});
//materials[c].normalScale.set(0.5,0.5);
}
}
var selectedMaterial = null;
function createSelectedMaterial() {
selectedMaterial = [];
var tiling = 4;
// common textures
var diff;
var norm = textures['texture/wood_N.jpg'].clone();
norm.tile(tiling);
var spec = textures['texture/wood_S.jpg'].clone();
spec.tile(tiling);
for(var c = 0; c<2; c++) {
diff = textures['texture/wood-1.jpg'].clone();
diff.tile(tiling);
//common material
selectedMaterial[c] = new THREE.MeshPhongMaterial({
color:0x00ff00,
emissive:0x009900,
specular:0x999999,
shininess:60.0,
wireframe:WIREFRAME,
transparent:false,
map:diff,
specularMap:spec,
normalMap:norm
//opacity:0.4
});
selectedMaterial[c].normalScale.set(0.3,0.3);
}
}

View File

@ -0,0 +1,279 @@
// ECMAScript 5 strict mode
/* jshint globalstrict: true*/
/* global THREE,console,BLACK,WHITE,WIREFRAME */
"use strict";
var pieceMaterial = [];
pieceMaterial[BLACK] = new THREE.MeshPhongMaterial({color:0x111111,specular:0xaaaaaa,shininess:30.0,wireframe:WIREFRAME});
pieceMaterial[WHITE] = new THREE.MeshPhongMaterial({color:0xdddddd,wireframe:WIREFRAME});
function createPawn(size,color) {
var pawn = new THREE.Object3D();
var baseHeight = size*0.1;
var baseRadius = size*0.8;
var base = new THREE.Mesh(
new THREE.CylinderGeometry(baseRadius,baseRadius,baseHeight,32,1),pieceMaterial[color]);
base.position.y = baseHeight/2;
var bodyRadius = size*0.6;
var body = new THREE.Mesh(
new THREE.SphereGeometry(bodyRadius,32,16),pieceMaterial[color]);
body.position.y = baseHeight + bodyRadius;
var neckHeight = size;
var neckRadius = bodyRadius*0.7;
var neck = new THREE.Mesh(
new THREE.CylinderGeometry(0,neckRadius,neckHeight,32,1),pieceMaterial[color]);
neck.position.y = baseHeight + bodyRadius*2+neckHeight/2;
var headRadius = size*0.2;
var head = new THREE.Mesh(
new THREE.SphereGeometry(headRadius,32,16),pieceMaterial[color]);
head.position.y = baseHeight + bodyRadius*2 +neckHeight+headRadius/2;
pawn.add(head);
pawn.add(neck);
pawn.add(body);
pawn.add(base);
pawn.name = "Pawn";
return pawn;
}
function createRook(size,color) {
var rook = new THREE.Object3D();
var baseHeight = size*0.1;
var baseRadius = size*0.8;
var base = new THREE.Mesh(
new THREE.CylinderGeometry(baseRadius,baseRadius,baseHeight,32,1),pieceMaterial[color]);
base.position.y = baseHeight/2;
var bodyRadius = size*0.4;
var bodyHeight = size*1.6;
var body = new THREE.Mesh(
new THREE.CylinderGeometry(bodyRadius*1.2,bodyRadius,bodyHeight,32,16),pieceMaterial[color]);
body.position.y = baseHeight + bodyHeight/2;
var wedgeHeight = size*0.5;
var wedgeRadius = bodyRadius * 1.5;
var wedge = new THREE.Mesh(
new THREE.CylinderGeometry(wedgeRadius,bodyRadius*1.2,wedgeHeight,32,1),pieceMaterial[color]);
wedge.position.y = baseHeight + bodyHeight+wedgeHeight/2;
var teethThickness = size*0.2;
var teethHeight = wedgeHeight;
var teethTilt = size*0.1;
var obr = wedgeRadius;
var ibr = wedgeRadius-teethThickness;
var otr = obr-teethTilt;
var itr = ibr-teethTilt;
var teethCount = 6;
var subdivision = Math.round(32/(teethCount*2));
var teethGeo = new THREE.TubeGeometry(otr,obr,itr,ibr,teethHeight,subdivision,1,0,2*Math.PI/(teethCount*2));
for (var i = 0; i < teethCount; i++) {
var teeth = new THREE.Mesh(teethGeo,pieceMaterial[color]);
teeth.position.y = baseHeight+bodyHeight+wedgeHeight+teethHeight/2;
teeth.rotation.y = i*2*Math.PI/teethCount;
rook.add(teeth);
}
rook.add(wedge);
rook.add(body);
rook.add(base);
rook.name = "Rook";
return rook;
}
function createKnight(size,color) {
var knight = new THREE.Object3D();
var baseHeight = size*0.1;
var baseRadius = size*0.8;
var base = new THREE.Mesh(
new THREE.CylinderGeometry(baseRadius,baseRadius,baseHeight,32,1),pieceMaterial[color]);
base.position.y = baseHeight/2;
var bodyRadius = size*0.6;
var body = new THREE.Mesh(
new THREE.SphereGeometry(bodyRadius,32,16),pieceMaterial[color]);
var neckHeight = size*1.2;
var neckBottomRadius = bodyRadius*0.6;
var neckTopRadius = bodyRadius*0.0;
var _neck = new THREE.Mesh(
new THREE.CylinderGeometry(neckTopRadius,neckBottomRadius,neckHeight,32,1),pieceMaterial[color]);
_neck.position.y = bodyRadius+neckHeight/2;
var neck = new THREE.Object3D();
neck.add(_neck);
neck.rotation.z = -Math.PI/32;
var head = new THREE.Object3D();
var skullRadius = size*0.6;
var skull = new THREE.Mesh(
new THREE.SphereGeometry(skullRadius,32,16),pieceMaterial[color]);
var faceHeight = size*1.0;
var faceBottomRadius = skullRadius*0.8;
var faceTopRadius = skullRadius*0.3;
var face = new THREE.Mesh(
new THREE.CylinderGeometry(faceTopRadius,faceBottomRadius,faceHeight,32,1),pieceMaterial[color]);
face.rotation.z = Math.PI/16;
face.position.y = skullRadius;
var noseRadius = size*0.2;
var nose = new THREE.Mesh(
new THREE.SphereGeometry(noseRadius,32,16),pieceMaterial[color]);
nose.position.y = skullRadius +faceHeight+noseRadius/2;
head.add(skull);
head.add(face);
//head.add(nose);
head.scale.z = 0.5;
head.rotation.z = 5*Math.PI/8;
head.position.y = bodyRadius+neckHeight;
var horse = new THREE.Object3D();
horse.add(body);
horse.add(neck);
horse.add(head);
horse.rotation.z = -Math.PI/32;
horse.rotation.y = (color == whiteMat) ? -Math.PI/2 : Math.PI/2;
horse.position.y = baseHeight+bodyRadius;
knight.add(horse);
knight.add(base);
knight.name = "Knight";
return knight;
}
function createBishop(size,color) {
var bishop = new THREE.Object3D();
var baseHeight = size*0.1;
var baseRadius = size*0.8;
var base = new THREE.Mesh(
new THREE.CylinderGeometry(baseRadius,baseRadius,baseHeight,32,1),pieceMaterial[color]);
base.position.y = baseHeight/2;
var bodyRadius = size*0.6;
var body = new THREE.Mesh(
new THREE.SphereGeometry(bodyRadius,32,16),pieceMaterial[color]);
body.position.y = baseHeight + bodyRadius;
var neckHeight = size*1.7;
var neckBottomRadius = bodyRadius*0.3;
var neckTopRadius = bodyRadius*0.05;
var neck = new THREE.Mesh(
new THREE.CylinderGeometry(neckTopRadius,neckBottomRadius,neckHeight,32,1),pieceMaterial[color]);
neck.position.y = baseHeight + bodyRadius*2+neckHeight/2;
var headRadius = size*0.2;
var head = new THREE.Mesh(
new THREE.SphereGeometry(headRadius,32,16),pieceMaterial[color]);
head.scale.y = 1.5;
head.position.y = baseHeight + bodyRadius*2 +neckHeight+headRadius/2;
bishop.add(head);
bishop.add(neck);
bishop.add(body);
bishop.add(base);
bishop.name = "Bishop";
return bishop;
}
function createQueen(size,color) {
var queen = new THREE.Object3D();
var baseHeight = size*0.1;
var baseRadius = size*0.8;
var base = new THREE.Mesh(
new THREE.CylinderGeometry(baseRadius,baseRadius,baseHeight,32,1),pieceMaterial[color]);
base.position.y = baseHeight/2;
var bodyRadius = size*0.6;
var body = new THREE.Mesh(
new THREE.SphereGeometry(bodyRadius,32,16),pieceMaterial[color]);
body.position.y = baseHeight + bodyRadius;
var neckHeight = size*2.3;
var neckBottomRadius = bodyRadius*0.3;
var neckTopRadius = bodyRadius*0.05;
var neck = new THREE.Mesh(
new THREE.CylinderGeometry(neckTopRadius,neckBottomRadius,neckHeight,32,1),pieceMaterial[color]);
neck.position.y = baseHeight + bodyRadius*2+neckHeight/2;
var collarHeight = size* 0.1;
var collarRadius = size* 0.4;
var collar = new THREE.Mesh(
new THREE.CylinderGeometry(collarRadius,collarRadius,collarHeight,32,1),pieceMaterial[color]);
collar.position.y = baseHeight+bodyRadius*2+neckHeight - size*0.1;
var headRadius = size*0.2;
var head = new THREE.Mesh(
new THREE.SphereGeometry(headRadius,32,16),pieceMaterial[color]);
head.position.y = baseHeight + bodyRadius*2 +neckHeight+headRadius/2;
queen.add(head);
queen.add(collar);
queen.add(neck);
queen.add(body);
queen.add(base);
queen.name = "Queen";
return queen;
}
function createKing(size,color) {
var king = new THREE.Object3D();
var baseHeight = size*0.1;
var baseRadius = size*0.8;
var base = new THREE.Mesh(
new THREE.CylinderGeometry(baseRadius,baseRadius,baseHeight,32,1),pieceMaterial[color]);
base.position.y = baseHeight/2;
var bodyRadius = size*0.6;
var body = new THREE.Mesh(
new THREE.SphereGeometry(bodyRadius,32,16),pieceMaterial[color]);
body.position.y = baseHeight + bodyRadius;
var neckHeight = size*2.3;
var neckBottomRadius = bodyRadius*0.15;
var neckTopRadius = bodyRadius*0.2;
var neck = new THREE.Mesh(
new THREE.CylinderGeometry(neckTopRadius,neckBottomRadius,neckHeight,32,1),pieceMaterial[color]);
neck.position.y = baseHeight + bodyRadius*2+neckHeight/2;
var collarHeight = size* 0.4;
var collarBottomRadius = size* 0.25;
var collarTopRadius = size* 0.45;
var collar = new THREE.Mesh(
new THREE.CylinderGeometry(collarTopRadius,collarBottomRadius,collarHeight,32,1),pieceMaterial[color]);
collar.position.y = baseHeight+bodyRadius*2+neckHeight - size*0.1;
var cross = new THREE.Object3D();
var crossThickness = size*0.2;
var crossHeight = size*0.8;
var crossGeo = new THREE.CubeGeometry(crossThickness,crossHeight,crossThickness);
var hCross = new THREE.Mesh(crossGeo,pieceMaterial[color]);
hCross.position.y = crossHeight/2;
var vCross = new THREE.Mesh(crossGeo,pieceMaterial[color]);
vCross.rotation.z = Math.PI/2;
vCross.position.y = crossHeight/2;
cross.add(hCross);
cross.add(vCross);
cross.position.y = baseHeight+bodyRadius*2 + neckHeight- size*0.1+collarHeight/2;
king.add(cross);
king.add(collar);
king.add(neck);
king.add(body);
king.add(base);
king.name = "King";
return king;
}

View File

@ -0,0 +1,545 @@
// ECMAScript 5 strict mode
/* jshint globalstrict: true*/
/* jslint newcap: true */
/* global THREE, $, document, window, console */
/* global LOADING_BAR_SCALE,ROWS,COLS,PIECE_SIZE, BOARD_SIZE, FLOOR_SIZE, WIREFRAME, DEBUG, Cell, WHITE, BLACK, FEEDBACK, SHADOW */
/* global SearchAndRedraw, UIPlayMove, camera, levels, g_allMoves:true, promotion:true, g_backgroundEngine:true, validMoves, InitializeBackgroundEngine, EnsureAnalysisStopped, newGame, redrawBoard, parsePGN, g_playerWhite:true */
/*global Search,FormatSquare,GenerateMove,MakeMove,GetMoveSAN,MakeSquare,UnmakeMove, FormatMove, ResetGame, GetFen, GetMoveFromString, alert, InitializeFromFen, GenerateValidMoves */
/*global g_inCheck,g_board,g_pieceList, g_toMove, g_timeout:true,g_maxply:true */
/*global moveflagCastleKing, moveflagCastleQueen, moveflagEPC, moveflagPromotion, colorWhite*/
/*global moveflagPromoteQueen,moveflagPromoteRook,moveflagPromoteBishop,moveflagPromoteKnight*/
/*global piecePawn, pieceKnight, pieceBishop, pieceRook, pieceQueen, pieceKing */
"use strict";
(function () {
// jQuery pgn textarea
var $pgn;
// list of moves in pgn format
var g_pgn = [];
// jQuery check feedback
var $info;
function initInfo() {
// create the DOM element
// to display Chc
$info = $("<div>")
.css("position","absolute")
.position({
of:$("body"),
my:"right top",
at:"right top"
})
.attr("id","info")
.appendTo($("body"))
.css("left","auto")
.css("right","0");
}
function initGUI() {
var $gui = $("<div>")
.css("position","absolute")
.position({
of:$("body"),
my:"left top",
at:"left top"
})
.width(150)
.attr("id","gui");
$("<p>")
.text("menu")
.appendTo($gui);
var $menudiv = $("<div>").appendTo($gui);
var $menu = $("<ul>").appendTo($menudiv);
makeButton("NewGame",newGameDialog,$menu);
makeButton("Undo",undo,$menu);
makeButton("Load",loadDialog,$menu);
makeButton("Save",save,$menu);
$("<label>")
.text("Promotion:")
.append(
$("<select>")
.width(140)
.append(
$("<option>")
.text("Queen"))
.append(
$("<option>")
.text("Rook"))
.append(
$("<option>")
.text("Bishop"))
.append(
$("<option>")
.text("Knight"))
.change( changePromo )
.appendTo($menudiv)
)
.appendTo($menudiv);
$pgn = $("<textarea>")
.attr("cols","16")
.attr("rows","10")
.attr("readonly","readonly")
.appendTo($menudiv);
$("body").append($gui);
$gui.accordion({
header: "p",
collapsible: true
});
}
function makeButton(name,callback,parent) {
var $item = $("<li>").appendTo(parent);
return $("<button>")
.button({
label:name
})
.width(140)
.click(callback)
.appendTo($item);
}
function newGameDialog() {
var id = "newgame";
var dialogColor = WHITE;
var dialogLevel = 0;
if ($("#"+id).length !== 0) {
return false;
}
var $newGame = $("<div>")
.attr("id",id)
.attr("title","New Game")
.appendTo($("body"));
// buttonset div
var $radio = $("<p>").appendTo($newGame);
// first button for white
$('<input type="radio" id="white" name="color" checked="checked">')
.click(function() {
dialogColor = WHITE;
})
.appendTo($radio);
$('<label for="white">White</label>').appendTo($radio);
// second button for black
$('<input type="radio" id="black" name="color"/>')
.click(function() {
dialogColor = BLACK;
})
.appendTo($radio);
$('<label for="black">Black</label>').appendTo($radio);
// initialize the buttonset
$radio.buttonset();
// level selector
var $label = $("<label>")
.text("AI Strength:");
var $levelSelect = $('<select>')
.css("display","block")
.appendTo($label)
.change(function(event) {
dialogLevel = $(event.currentTarget).val();
});
$("<p>").append($label).appendTo($newGame);
// add as much level configuration we have
for (var i = 0; i < levels.length; i++) {
$('<option>')
.val(i)
.text("level "+(i+1))
.appendTo($levelSelect);
}
$newGame.dialog({
close:function(event,ui) {
$newGame.remove();
},
buttons: {
"Start": function() {
newGame(dialogColor,dialogLevel);
$(this).remove();
}
}
});
}
/*
* GAME CONTROL
*/
function newGame(color,level) {
// change AI parameters according to level
if (levels[level] !== undefined) {
g_timeout = levels[level].timeout;
g_maxply = levels[level].maxply;
}
EnsureAnalysisStopped();
ResetGame();
if (InitializeBackgroundEngine()) {
g_backgroundEngine.postMessage("go");
}
g_allMoves = [];
clearPGN();
redrawBoard();
if (color === WHITE) {
g_playerWhite = true;
camera.position.x = 0;
camera.position.z = 100; // camera on white side
} else {
g_playerWhite = false;
SearchAndRedraw();
camera.position.x = 0;
camera.position.z = -100; // camera on black side
}
}
function changeStartPlayer(event) {
g_playerWhite = $(event.currentTarget).val() === "white";
redrawBoard();
}
function undo() {
if (g_allMoves.length === 0) {
return;
}
if (g_backgroundEngine !== null) {
g_backgroundEngine.terminate();
g_backgroundEngine = null;
}
UnmakeMove(g_allMoves[g_allMoves.length - 1]);
g_allMoves.pop();
g_pgn.pop();
g_pgn.pop();
updatePGN();
if (g_playerWhite !== Boolean(g_toMove) && g_allMoves.length !== 0) {
UnmakeMove(g_allMoves[g_allMoves.length - 1]);
g_allMoves.pop();
}
redrawBoard();
}
function loadDialog() {
var id = "loadGame";
if ($("#"+id).length !== 0) {
return false;
}
var $loadGame = $("<div>")
.attr("id",id)
.attr("title","Load Game")
.appendTo($("body"));
$('<input>')
.attr("type","file")
.change(function(evt) {
load(evt);
$loadGame.remove();
})
.appendTo($loadGame);
$loadGame
.dialog({
minWidth:420,
close:function(event,ui) {
$loadGame.remove();
}
});
}
function load(evt) {
//Retrieve the first (and only!) File from the FileList object
var file = evt.target.files[0];
if (file) {
var reader = new FileReader();
reader.onload = function(e) {
var contents = e.target.result;
loadPGN(contents);
};
reader.readAsText(file);
} else {
console.log("Failed to load file");
}
}
function loadFEN(fen) {
g_allMoves = [];
InitializeFromFen(fen);
EnsureAnalysisStopped();
InitializeBackgroundEngine();
g_playerWhite = !!g_toMove;
g_backgroundEngine.postMessage("position " + GetFen());
redrawBoard();
}
function loadPGN (pgn) {
var parsedPGN = parsePGN(pgn);
var fen = parsedPGN.fen;
var moves = parsedPGN.sequence;
g_allMoves = [];
clearPGN();
if (fen !== null) {
loadFEN(fen);
if (parsedPGN.startColor === BLACK) {
g_pgn.push("..");
}
} else {
ResetGame();
}
function Piece(flag,promo) {
this.flag = flag;
this.promo = promo;
}
moves.forEach(function(move) {
var i;
var formatedMove;
var vMoves = GenerateValidMoves();
var pieces = {
"P": new Piece(piecePawn,null),
"N": new Piece(pieceKnight,moveflagPromoteKnight),
"B": new Piece(pieceBishop,moveflagPromoteBishop),
"R": new Piece(pieceRook,moveflagPromoteRook),
"Q": new Piece(pieceQueen,moveflagPromoteQueen),
"K": new Piece(pieceKing,null)
};
// get the piece flag
var piece = pieces[move.piece].flag; // [P,N,B,R,Q,K]
// ge the color flag
var color = (move.color === WHITE) ? 0x8 : 0x0;
// get the from value
var startList = [];
// get all square that has this kind of piece
var pieceIdx = (color|piece) << 4;
while(g_pieceList[pieceIdx] !== 0) {
startList.push(new Cell(FormatSquare(g_pieceList[pieceIdx])));
pieceIdx++;
}
var from = move.from;
if (from !== undefined) {
// if we have a precision on the starting square like the columns
// or even the position directly
// We will filter the startList using it
for (i = startList.length - 1; i >= 0; i--) {
if( from.length === 1) {
// only the row is given
if (from.match(/[a-h]/) && startList[i].position.charAt(0) !== from) {
// different starting row
startList.splice(i,1);
} else if (from.match(/[1-8]/) && startList[i].position.charAt(1) !== from) {
// different starting line
startList.splice(i,1);
}
} else if (from.length === 2) {
// the starting coordinate is given
// this is then just an extra check
if (startList[i].position !== from) {
// different starting coordinate
startList.splice(i,1);
}
}
}
}
// here we should have a list of starting square
// only one should make a valid move
// paired with the provided destination
var end = new Cell(move.to);
var endSquare = MakeSquare(end.y, end.x);
var promotion = (move.promotion) ? pieces[move.promotion.substr(1)].promo : undefined; // remove the "="
// take formatedMove and endSquare in a closure
function checkMove(start) {
var startSquare = MakeSquare(start.y, start.x);
if (promotion !== undefined) {
// we have a promotion so we need to generate a
// specific move and check against it
if(vMoves[i] === GenerateMove(startSquare, endSquare, moveflagPromotion | promotion)) {
formatedMove = vMoves[i];
}
} else {
// just checking start and end square allows to cover
// all other special moves like "en passant" capture and
// castling
if ( (vMoves[i] & 0xFF) == startSquare &&
((vMoves[i] >> 8) & 0xFF) == endSquare ) {
formatedMove = vMoves[i];
}
}
}
// to get the move we will check withing all valide moves
// which one match the hints given by the pgn
for (i = 0; i < vMoves.length; i++) {
startList.forEach(checkMove);
if (formatedMove) break;
}
if(formatedMove) {
UIPlayMove(formatedMove,false);
} else {
console.log(move);
throw "Invalid PGN";
}
});
if (g_toMove === colorWhite) {
g_playerWhite = true;
camera.position.x = 0;
camera.position.z = 100;
} else {
g_playerWhite = false;
camera.position.x = 0;
camera.position.z = -100;
}
EnsureAnalysisStopped();
if (InitializeBackgroundEngine()) {
g_backgroundEngine.postMessage("position " + GetFen());
}
redrawBoard();
}
function clearPGN () {
$pgn.val("");
g_pgn = [];
}
function addToPGN(move) {
g_pgn.push(GetMoveSAN(move));
updatePGN();
}
function updatePGN() {
$pgn.val(getPGN());
$pgn.scrollTop($pgn[0].scrollHeight);
}
function getPGN() {
var str = "";
g_pgn.forEach(function(move,i) {
if(i%2 === 0) {
if (move === "..") {
str += ((i/2)+1)+"...";
} else {
str += ((i/2)+1)+". "+move;
}
} else {
str += " "+move+"\r\n";
}
});
return str;
}
function save() {
var filename = "chessSave.pgn";
var a = document.createElement("a");
if (typeof a.download === "undefined")
{
var str = 'data:text/html,' + encodeURIComponent("<p><a download='" + filename + "' href=\"data:application/json," +
encodeURIComponent(getPGN()) +
"\">Download link</a></p>");
window.open(str);
} else {
// auto download
var body = document.body;
a.textContent = filename;
a.href = "data:application/json," + encodeURIComponent(getPGN());
a.download = filename;
body.appendChild(a);
var clickEvent = document.createEvent("MouseEvent");
clickEvent.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
a.dispatchEvent(clickEvent);
body.removeChild(a);
}
}
function changePromo(event) {
var choice = $(event.currentTarget).val();
switch(choice) {
case "Queen":
promotion = moveflagPromoteQueen;
break;
case "Rook":
promotion = moveflagPromoteRook;
break;
case "Bishop":
promotion = moveflagPromoteBishop;
break;
case "Knight":
promotion = moveflagPromoteKnight;
break;
}
}
function displayCheck() {
if (validMoves.length === 0) {
$info.text(( g_inCheck ? 'Checkmate' : 'Stalemate' ));
} else if (g_inCheck) {
$info.text('Check');
} else {
$info.text('');
}
if ($info.text() !== '') {
$info.show("highlight",{},500);
} else {
$info.hide();
}
}
window.initGUI = initGUI;
window.initInfo = initInfo;
window.clearPGN = clearPGN;
window.addToPGN = addToPGN;
window.displayCheck = displayCheck;
window.newGame = newGame;
})();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,456 @@
/**
* @author qiao / https://github.com/qiao
* @author mrdoob / http://mrdoob.com
* @author alteredq / http://alteredqualia.com/
* @author WestLangley / https://github.com/WestLangley
* @author erich666 / http://erichaines.com
*
* Not quite correct: sort of assumes a 45 degree field of view.
* Currently uses distance to target for offsetting
*/
THREE.OrbitAndPanControls = function ( object, domElement ) {
this.addEventListener = THREE.EventDispatcher.prototype.addEventListener;
this.hasEventListener = THREE.EventDispatcher.prototype.hasEventListener;
this.removeEventListener = THREE.EventDispatcher.prototype.removeEventListener;
this.dispatchEvent = THREE.EventDispatcher.prototype.dispatchEvent;
this.enabled = true;
this.object = object;
this.domElement = ( domElement !== undefined ) ? domElement : document;
// API
this.target = new THREE.Vector3();
this.userZoom = true;
this.userZoomSpeed = 1.0;
this.userRotate = true;
this.userRotateSpeed = 1.0;
this.autoRotate = false;
this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
this.minPolarAngle = 0; // radians
this.maxPolarAngle = Math.PI; // radians
this.minDistance = 0;
this.maxDistance = Infinity;
// internals
var scope = this;
var EPS = 0.000001;
var rotateStart = new THREE.Vector2();
var rotateEnd = new THREE.Vector2();
var rotateDelta = new THREE.Vector2();
var panStart = new THREE.Vector2();
var panEnd = new THREE.Vector2();
var panDelta = new THREE.Vector2();
var dollyStart = new THREE.Vector2();
var dollyEnd = new THREE.Vector2();
var dollyDelta = new THREE.Vector2();
var phiDelta = 0;
var thetaDelta = 0;
var scale = 1;
var pan = new THREE.Vector3();
var lastPosition = new THREE.Vector3();
var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
var state = STATE.NONE;
// events
var changeEvent = { type: 'change' };
this.rotateLeft = function ( angle ) {
if ( angle === undefined ) {
angle = getAutoRotationAngle();
}
thetaDelta -= angle;
};
this.rotateUp = function ( angle ) {
if ( angle === undefined ) {
angle = getAutoRotationAngle();
}
phiDelta -= angle;
};
this.panLeft = function ( distance ) {
var panOffset = new THREE.Vector3();
var te = this.object.matrix.elements;
// get X column of matrix
panOffset.set( te[0], te[1], te[2] );
panOffset.multiplyScalar(-distance);
pan.add( panOffset );
};
this.panUp = function ( distance ) {
var panOffset = new THREE.Vector3();
var te = this.object.matrix.elements;
// get Y column of matrix
panOffset.set( te[4], te[5], te[6] );
panOffset.multiplyScalar(distance);
pan.add( panOffset );
};
this.dollyIn = function ( dollyScale ) {
if ( dollyScale === undefined ) {
dollyScale = getZoomScale();
}
scale /= dollyScale;
};
this.dollyOut = function ( dollyScale ) {
if ( dollyScale === undefined ) {
dollyScale = getZoomScale();
}
scale *= dollyScale;
};
this.update = function () {
var position = this.object.position;
var offset = position.clone().sub( this.target );
// angle from z-axis around y-axis
var theta = Math.atan2( offset.x, offset.z );
// angle from y-axis
var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
if ( this.autoRotate ) {
this.rotateLeft( getAutoRotationAngle() );
}
theta += thetaDelta;
phi += phiDelta;
// restrict phi to be between desired limits
phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
// restrict phi to be betwee EPS and PI-EPS
phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
var radius = offset.length() * scale;
// restrict radius to be between desired limits
radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
// move target to panned location
this.target.add( pan );
offset.x = radius * Math.sin( phi ) * Math.sin( theta );
offset.y = radius * Math.cos( phi );
offset.z = radius * Math.sin( phi ) * Math.cos( theta );
position.copy( this.target ).add( offset );
this.object.lookAt( this.target );
thetaDelta = 0;
phiDelta = 0;
scale = 1;
pan.set(0,0,0);
if ( lastPosition.distanceTo( this.object.position ) > 0 ) {
this.dispatchEvent( changeEvent );
lastPosition.copy( this.object.position );
}
};
function getAutoRotationAngle() {
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
}
function getZoomScale() {
return Math.pow( 0.95, scope.userZoomSpeed );
}
function onMouseDown( event ) {
if ( scope.enabled === false ) { return; }
if ( !scope.userRotate ) { return; }
event.preventDefault();
if ( event.button === 0 ) {
state = STATE.ROTATE;
rotateStart.set( event.clientX, event.clientY );
} /*else if ( event.button === 2 ) {
state = STATE.PAN;
panStart.set( event.clientX, event.clientY );
}*/ else if ( event.button === 2 ) {
state = STATE.DOLLY;
dollyStart.set( event.clientX, event.clientY );
}
document.addEventListener( 'mousemove', onMouseMove, false );
document.addEventListener( 'mouseup', onMouseUp, false );
}
function onMouseMove( event ) {
if ( scope.enabled === false ) { return; }
event.preventDefault();
if ( state === STATE.ROTATE ) {
rotateEnd.set( event.clientX, event.clientY );
rotateDelta.subVectors( rotateEnd, rotateStart );
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / scope.domElement.width * scope.userRotateSpeed );
scope.rotateUp( Math.PI * rotateDelta.y / scope.domElement.height * scope.userRotateSpeed );
rotateStart.copy( rotateEnd );
} else if ( state === STATE.PAN ) {
panEnd.set( event.clientX, event.clientY );
panDelta.subVectors( panEnd, panStart );
var position = scope.object.position;
var offset = position.clone().sub( scope.target );
var targetDistance = offset.length();
if ( scope.object.fov !== undefined )
{
// half of the fov is center to top of screen
targetDistance *= Math.tan( (scope.object.fov/2) * Math.PI / 180.0 );
}
// we actually don't use screenWidth, since perspective camera is fixed to screen height
scope.panLeft( 2 * panDelta.x * targetDistance / scope.domElement.height );
scope.panUp( 2 * panDelta.y * targetDistance / scope.domElement.height );
panStart.copy( panEnd );
} else if ( state === STATE.DOLLY ) {
dollyEnd.set( event.clientX, event.clientY );
dollyDelta.subVectors( dollyEnd, dollyStart );
if ( dollyDelta.y > 0 ) {
scope.dollyIn();
} else {
scope.dollyOut();
}
dollyStart.copy( dollyEnd );
}
}
function onMouseUp( /* event */ ) {
if ( scope.enabled === false ) { return; }
if ( ! scope.userRotate ) { return; }
document.removeEventListener( 'mousemove', onMouseMove, false );
document.removeEventListener( 'mouseup', onMouseUp, false );
state = STATE.NONE;
}
function onMouseWheel( event ) {
if ( scope.enabled === false ) { return; }
if ( ! scope.userZoom ) { return; }
var delta = 0;
if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
delta = event.wheelDelta;
} else if ( event.detail ) { // Firefox
delta = - event.detail;
}
if ( delta > 0 ) {
scope.dollyOut();
} else {
scope.dollyIn();
}
}
function touchstart( event ) {
if ( scope.enabled === false ) { return; }
/* TODO, innards of mouse begin here
switch ( event.touches.length ) {
case 1:
state = STATE.TOUCH_ROTATE;
rotateStart.set( event.clientX, event.clientY );
_state = STATE.TOUCH_ROTATE;
_rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
break;
case 2:
state = STATE.TOUCH_DOLLY;
dollyStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
// classier would be to take both points for a multitouch stretch thing:
//var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
//var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
//_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
break;
case 3:
state = STATE.TOUCH_PAN;
panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
break;
default:
state = STATE.NONE;
}
*/
}
function touchmove( event ) {
if ( scope.enabled === false ) { return; }
event.preventDefault();
event.stopPropagation();
/* TODO, innards of mouse move here
switch ( event.touches.length ) {
case 1:
_rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
break;
case 2:
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy )
break;
case 3:
_panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
break;
default:
_state = STATE.NONE;
}
*/
}
function touchend( event ) {
if ( scope.enabled === false ) { return; }
/* from trackball. I don't think we need anything.
switch ( event.touches.length ) {
case 1:
_rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
break;
case 2:
_touchZoomDistanceStart = _touchZoomDistanceEnd = 0;
break;
case 3:
_panStart = _panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
break;
}
*/
state = STATE.NONE;
}
this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
this.domElement.addEventListener( 'mousedown', onMouseDown, false );
this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox
this.domElement.addEventListener( 'touchstart', touchstart, false );
this.domElement.addEventListener( 'touchend', touchend, false );
this.domElement.addEventListener( 'touchmove', touchmove, false );
};

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,308 @@
// ECMAScript 5 strict mode
/* jshint globalstrict: true*/
/* global THREE, $, document, window, console */
/* global onLoaded, LOADING_BAR_SCALE,ROWS,COLS,PIECE_SIZE, BOARD_SIZE, FLOOR_SIZE, WIREFRAME, DEBUG, Cell, WHITE, BLACK, FEEDBACK, SHADOW */
"use strict";
var geometries = {};
var textures = {};
(function() {
var $bar,$tips;
var glow;
function loadResources () {
// counter
var loaded = 0;
// list of all mesh and texture
var resources = [
'3D/json/knight.json',
'3D/json/king.json',
'3D/json/queen.json',
'3D/json/bishop.json',
'3D/json/rook.json',
'3D/json/pawn.json',
'3D/json/board.json',
'3D/json/innerBoard.json',
'texture/wood-0.jpg',
'texture/wood-1.jpg',
'texture/wood_N.jpg',
'texture/wood_S.jpg',
'texture/knight-ao.jpg',
'texture/rook-ao.jpg',
'texture/king-ao.jpg',
'texture/bishop-ao.jpg',
'texture/queen-ao.jpg',
'texture/pawn-ao.jpg',
'texture/floor.jpg',
'texture/floor_N.jpg',
'texture/floor_S.jpg',
'texture/fakeShadow.jpg'
];
// for loading mesh
function loadJSON (url) {
var loader = new THREE.JSONLoader();
loader.load(url, function(geometry) {
geometries[url] = geometry;
loaded++;
checkLoad();
});
}
// for loading texture
function loadImage(url) {
THREE.ImageUtils.loadTexture(
url,
THREE.UVMapping(),
function(texture) {
textures[url] = texture;
loaded++;
checkLoad();
}
);
}
// load all the resources from the list
resources.forEach(function(url) {
switch ( url.split('.').pop() ) {
case 'json' :
loadJSON(url);
break;
case 'jpg' :
loadImage(url);
break;
default:
throw 'invalid resource';
}
});
// control the progressBar
// and fire the onLoaded call back on completion
function checkLoad () {
$bar.update(loaded/resources.length);
if (loaded === resources.length) {
setTimeout(onLoaded,0.1);
}
}
}
function initGlow() {
// create and set the green glow in the background
var size = window.innerWidth*LOADING_BAR_SCALE*1.8;
glow = document.createElement('canvas');
glow.width = size;
glow.height = size;
document.body.appendChild(glow);
var ctx = glow.getContext('2d');
// make it oval
glow.style.width = size + "px";
glow.style.height = Math.round(size/2) + "px";
var requestId;
function animate() {
var dt = getDelta();
update(dt);
requestId = window.requestAnimationFrame(animate);
}
function update(dt) {
ctx.clearRect(0,0,size,size);
// for the pulse effect
var cycle = Math.cos(Date.now()/1000 * Math.PI);
var maxRadius = size/2.5;
function lerp(a,b,p) {
return a + (b-a)*p;
}
var amplitude = maxRadius * 0.015;
var sizeOffset = cycle*amplitude;
var radius = maxRadius - amplitude + sizeOffset;
var saturation = lerp(70,100,(cycle+1)/2);
var grd = ctx.createRadialGradient(size/2, size/2, 0, size/2, size/2, radius);
// fake a non linear gradient
grd.addColorStop(0, 'hsla(90,'+saturation+'%,50%,0.5)');
grd.addColorStop(0.125,'hsla(90,'+saturation+'%,50%,0.3828125)');
grd.addColorStop(0.25, 'hsla(90,'+saturation+'%,50%,0.28125)');
grd.addColorStop(0.375,'hsla(90,'+saturation+'%,50%,0.1953125)');
grd.addColorStop(0.5, 'hsla(90,'+saturation+'%,50%,0.125)');
grd.addColorStop(0.75, 'hsla(90,'+saturation+'%,50%,0.03125)');
grd.addColorStop(1, 'hsla(90,'+saturation+'%,50%,0.0)');
// draw the gradient
ctx.rect(0,0,size,size);
ctx.fillStyle = grd;
ctx.fill();
}
glow.remove = function() {
window.cancelAnimationFrame(requestId);
this.parentNode.removeChild(this);
};
var oldTime;
function getDelta() {
var now = Date.now();
if (oldTime === undefined) {
oldTime = now;
}
var delta = (now - oldTime)/1000;
oldTime = now;
return delta;
}
animate();
}
function initTips() {
// list of tips
var tips = [
"Aggregating wood fibers",
"Generating pieces census report",
"Testing board resistance",
"Generating Matrix 8x8",
"Answering Queen's request",
"Carving a princess for the knight",
"Sanding the Bishop",
"Enrolling Pawns",
"Generating cheat sheet",
"Mating the king",
"Planting virtual trees",
"Asking Deep Blue for advice",
"Nominating Bishops",
"Dubbing Knights",
"Crowning the King",
"Waxing chessboard",
"Evaluating the idea of an hexagonal board, and rejecting it",
"Gathering extra vertices (just in case)",
"Trimming edges",
"Intimidating opponent",
"Learning the rules"
];
//jQuery object for tips
$tips = $('<div>')
.attr("id","tips")
.css("color","white")
.appendTo($('body'));
// how often tips changes (in ms)
var tipTiming = 5000;
$tips.update = function() {
var self = this;
if( tips.length > 0 ) {
var index = Math.floor(Math.random() * tips.length);
var sentence = tips[index];
tips.splice(index,1);
$(this).text(sentence+"...");
}
this.timer = setTimeout(function(){self.update();},tipTiming);
};
// this little ugliness is just to clear the timer
// automagically on .remove()
var tipsRemove = $tips.remove;
$tips.remove = function() {
clearTimeout(this.timer);
tipsRemove.call(this);
};
$tips.update();
}
function initBar() {
// jQuery progress bar
$bar = $('<div>')
.attr("id","progressbar")
.css("width",(LOADING_BAR_SCALE*100)+"%")
.appendTo($('body'));
// jQuery progress bar label
var $label = $('<div>')
.attr("id","progress-label")
.appendTo($bar);
// setting up the progressbar
$bar.progressbar({
value:false,
change: function() {
$label.text($bar.progressbar("value") + "%");
}
});
// avoid rounded corners
$bar.removeClass('ui-corner-all');
$bar.children().removeClass('ui-corner-all');
$bar.children().removeClass('ui-corner-left');
// that's where the progression happens
$bar.update = function(p) {
p = Math.round(p*100);
$bar.progressbar( "value", p );
// somehow need to constantly remove it
$bar.children().removeClass('ui-corner-right');
};
$bar.update(0);
}
function centering() {
$bar.position({
of:window,
my:"center center",
at:"center center"
});
$tips.position({
of:$bar,
my:"center bottom",
at:"center top-10"
});
$(glow).position({
of:window,
my:"center center",
at:"center center"
});
window.addEventListener('resize',centering );
}
function removeLoader() {
$bar.remove();
$tips.remove();
glow.remove();
window.removeEventListener('resize',centering );
}
window.onload = function () {
// the page is loaded
// start the resource loader
initGlow();
initTips();
initBar();
centering();
loadResources();
//$bar.update(1);
};
window.removeLoader = removeLoader;
})();

View File

@ -0,0 +1,122 @@
// ECMAScript 5 strict mode
/* jshint globalstrict: true*/
/* global WHITE,BLACK */
"use strict";
(function() {
/*
var WHITE = 1;
var BLACK = 0;
*/
function Move(piece,color,from,to,promotion,result,str) {
this.piece = piece ? piece : "P";
this.color = color;
this.from = from;
this.to = to;
this.promotion = promotion;
this.result = result;
this.str = str;
}
String.prototype.removeBrackets = function(open,close) {
var count = 0;
var newString = "";
for (var i = 0; i < this.length; i++) {
var c = this.charAt(i);
if (c === open) {
count++;
continue;
}
if (c === close) {
count--;
continue;
}
if (count === 0) {
newString += c;
}
}
return newString;
};
function parsePGN(pgn) {
var moves = {};
moves.fen = null;
moves.sequence = [];
moves.startColor = WHITE;
var color = WHITE;
//var re_fen = /[pnbrqkPNBRQK1-8]+(\/[pnbrqkPNBRQK1-8]+){7} +[wb] +([KQ]{1,2}|-) *([kq]{1,2}|-)( +(([a-h][1-8])|-))? +\d+ +\d+/
var re_fen = /\[FEN *" *([pnbrqkPNBRQK1-8]+(?:\/[pnbrqkPNBRQK1-8]+){7} +([wb]) +(?:[KQ]{1,2}|-) *(?:[kq]{1,2}|-)(?: +(?:(?:[a-h][1-8])|-))? +\d+ +\d+) *" *\]/;
var match = pgn.match(re_fen);
if (match) {
moves.fen = match[1];
color = match[2] === "w" ? WHITE : BLACK;
moves.startColor = color;
}
var cleanPGN = pgn
.removeBrackets("[","]") // removes metadata
.removeBrackets("{","}") // removes comments
.removeBrackets("(",")") // removes comments
.replace(/\$\d+/g,'') // removes this thing
.replace(/\d+\.{1,3}/g,'') // removes move numbers
.replace(/\s+/g,' ') // replaces multiple whitespaces by simple spaces
.trim() // removes front and back whitespaces
.replace(/(0-1)$/g,'') // result black won
.replace(/(1-0)$/g,'') // result white won
.replace(/(1\/2-1\/2)$/g,'') // result draw
.replace(/(\*)$/g,'') // result ongoing
.trim()
.split(' '); // split moves
// regex for basic moves
// |pieces | |src col/row| |dest col/row| promo |check|
var re_pieceMove =/^([NBRQK])?([a-h]?[1-8]?)?x?([a-h][1-8])(=[NBRQK])?([+#])?/;
// regex for castling
var re_castling = "(O-O(?:-O)?)([+#])?";
var castling = {
"O-O" : {
from:['e8','e1'],
to:['g8','g1']
},
"O-O-O": {
from:['e8','e1'],
to:['c8','c1']
}
};
cleanPGN.forEach(function(move) {
var info=[];
info = move.match(re_pieceMove);
if(info) {
moves.sequence.push(new Move(
info[1],
color,
info[2],
info[3],
info[4],
info[5],
move
));
}
info = move.match(re_castling);
if(info) {
moves.sequence.push(new Move(
"K",
color,
castling[info[1]].from[color],
castling[info[1]].to[color],
undefined,
info[2],
move
));
}
color = 1-color;
});
return moves;
}
window.parsePGN = parsePGN;
})();

View File

@ -0,0 +1,27 @@
// ECMAScript 5 strict mode
/* jshint globalstrict: true*/
/* global SHADOW,onOBJLoaded,THREE,console,BLACK,WHITE,WIREFRAME */
/*
* few extension or modification to some three.js functionnality
* often to avoid some repetitive tasks
*/
"use strict";
(function () {
var clone = THREE.Texture.prototype.clone;
THREE.Texture.prototype.clone = function( texture ) {
var newTexture = clone.call(this,texture);
// The purpose of all this is to automagically switch this
// property to true after cloning.
// Since it originally doesn't do it by default
newTexture.needsUpdate = true;
return newTexture;
};
THREE.Texture.prototype.tile = function( factor ) {
// because I do that a lot (:
this.wrapS = this.wrapT = THREE.RepeatWrapping;
this.repeat.set(factor,factor);
};
})();