I am trying to implement a chess game in javascript. I created a chessboard and a rook
let board1 = [
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, "br", 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0],
];
//the following function creates a cheesboard:
function createBoard(board, boardEl) {
board.forEach((row, i) => {
row.forEach((col, j) => {
const square = document.createElement("div");
boardEl.appendChild(square);
const piece = document.createElement("img");
if (col) {
piece.src = `pieces/${col}.png`;
piece.style.userSelect = "none";
piece.dataset.color = col[0];
piece.dataset.symbol = col[1];
piece.dataset.col = j;
piece.dataset.row = i;
square.appendChild(piece);
}
square.style.display = "flex";
square.style.alignItems = "center";
square.style.justifyContent = "center";
square.style.userSelect = "none";
square.dataset.row = i;
square.dataset.col = j;
if (i % 2 == j % 2) square.style.backgroundColor = "green";
else square.style.backgroundColor = "white";
});
});
}
<body
style="
display: flex;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
background-color: white;
overflow:hidden;
"
>
<div
class="board"
style="
width: 560px;
height: 560px;
display: grid;
grid-template-columns: repeat(8, 1fr);
grid-template-rows: repeat(8, 1fr);
border: 1px solid white;
"
></div>
</body>
and the following event Listeners are responsible for dragging the piece in the chessboard
let draggedPiece;
let currentPiece;
document.addEventListener("dragstart", function (event) {
draggedPiece = event.target;
const { row, col, color, symbol } = draggedPiece.dataset;
currentPiece = new Piece(row, col, color, symbol);
console.log(currentPiece);
});
document.addEventListener("drop", function (event) {
if (
currentPiece.possibleMoves.find(
(square) =>
square.row == event.target.dataset.row &&
square.col == event.target.dataset.col
)
) {
draggedPiece.parentNode.removeChild(draggedPiece);
event.target.appendChild(draggedPiece);
currentPiece.updateCoords(
event.target.dataset.row,
event.target.dataset.col
);
currentPiece.getPossibleMoves();
}
});
document.addEventListener("dragover", function (event) {
event.preventDefault();
});
in the drop eventListener I am check if the piece can move to the square that we want to drop to;
but when I update the the coords of the current piece they don't change. By the way here is The piece class:
class Piece {
constructor(row, col, color, symbol) {
this.row = row;
this.col = col;
this.color = color;
this.symbol = symbol;
this.possibleMoves = [];
this.getPossibleMoves();
}
getPossibleMoves() {
this.possibleMoves = [];
for (let i = 0; i < 8; i++) {
if (i != this.col) {
this.possibleMoves.push({ row: i, col: Number(this.col) });
this.possibleMoves.push({ row: Number(this.row), col: i });
}
}
}
updateCoords(row, col){
this.row = row;
this.col = col;
}
}
Can you find some improvement on my code and help me solve this problem. Thank You.
Note: can you provide some hints, guidances and reference to help me in the creation of this nice game, And I don't know if the image will display when you run the code.
Here are some other things to consider while programming:
At the least you should have more classes: Board, Square, base class Piece from which you derive Rook, Bishop, Knight, etc...
Boards:
How a Board is represented internally (maybe as a 1 dimensional array) has no bearing on how it's drawn (2 dimensionally). Let the html and css worry about that.
Boards draw Squares.
Squares
Have a fixed location on a Board
Squares draw Pieces.
Pieces
know their current Square
All Pieces move But derived Pieces determine how
Rooks, Knights, etc (derived Pieces)
Know their own legal moves. eg: a Rook cannot move through an occupied Square.
Can draw themselves (.png)
You could have piece classes for each piece instead of one piece class. Although you don't necessarily need separate classes for each piece type, you could get away with three:
Pawns,
Jumping pieces (kings and knights), and
Sliding pieces (rooks, bishops, and queens)
The move generation breaks down nicely in that way. Pawn movement is completely unique to pawns plus you have to handle move generation for double pushes from their starting rank, en passant, and promotions. Kings and knights move in the exact same way, they just target different squares. Similarly, rooks, bishops, and queens all move in the same basic way, just in different directions.
This way a king is created by injecting a king attack map into a jumping piece class, while a knight is created by injecting a knight attack map into the same class.
From an OOP perspective, it seems natural that a piece should keep track of where it is, however in practice this doesn't work very well (In general, OOP isn't the best way to approach chess programming, but it can work). In order to generate moves for a piece, you need to know both where the piece is, and where all the other pieces are. So can pick one of three options either:
the piece knows where it is and doesn't know how it moves, i.e., you have a separate class that contains movement rules for all pieces and applies it by extracting position information from piece classes, or
the piece knows where it is and how it moves, which would require very complex relationships in communicating positions between the various piece classes, or
the piece knows how it moves but not where it is, and position information is fed in from outside.
For option one, it doesn't really make sense to have a class that only knows where the piece is, there are better ways to store this information if that is all the class is doing. Option two, as described above, becomes very clunky and convoluted whenever I have tried to implement it. So, I think that option three is the best. The board knows where the pieces are, and the piece classes do not represent the pieces themselves, but instead represent the movement rules for those pieces.
I think it makes more sense to have a board class that tracks the pieces, piece classes that hold movement rules, and a move generator class that holds the rules for each piece. When the move generator wants to generate moves for a particular piece it can look at the board and see where the pieces of that type are (as well as other required information such as empty squares, opponent pieces, ep target, and castling rights) then reaches to its shelf of rule books (piece classes) gets the movement rules for the required piece and feeds in the information it obtained from the board.
The downside here is that you have piece classes that contain no data at all, just methods that implement the rules. So you would have to ask yourself if maybe they are better implemented as functions. Although, the class option does give you the option of run time polymorphism in rule generation, which you may find beneficial.
In terms of board representation, a good option, to begin with, are bitboards (which you can read about here). I don't know much Javascript and don't know about how bit twiddling works for Javascript. However, even if you are not comfortable with using a single integer to represent a particular view of the entire board you could do something similar with a 64-element array of 0s and 1s.
Related
I am currently working on a CMS project to create perspective objects using CSS transform properties (scale, rotate, translate...) in 3d: XYZ.
When an object is created it has these characteristics in its matrix3d(-16 values-) returned by window.getComputedStyle(element) -> style['transform']
The next step is to create animations: through a set of input[range], the user can modify these objects' characteristics and get their final state once the animation is finished.
It works very well. The question now is to create the complete dynamic CSS system, object by object.
I have its matrix1 (initial matrix3d) and its matrix2 (final matrix3d). By executing
#keyframes anim {
from { transform: matrix1 }
to { transform: matrix2; }
}
document.getElementById('object').style.animation = 'anim 2s linear';
it should work on its own... The prob is to create the CSS instruction. Using:
document.styleSheets[0].insertRule('
#keyframes anim {
from { transform: matrix1 }
to { transform: matrix2; }
}'
);
works well but once it's created in the main CSS file, if user modifies the matrix2 I can't rewrite the same instruction twice...
My idea is to create a CSS file for each objet on the server then import it with JS: if user is satisfied I keep it, and if not I remove it and create a new one with its new matrix2.
the advantage is that I can keep the word 'anim' without risking conflict between objects since each will call its own CSS (i.e 'object1.css').
Is this the best way to proceed or do you recommend another one?
Another question: despite my research I can't find what the 16 values of matrix3d correspond to. Translate XYZ is in [12], [13], [14] but I don't have them all. If you know a more explicit resource than https://developer.mozilla.org/fr/docs/Web/CSS/transform-function/matrix3d() it may help.
Finally found the solution... maybe not the best but it works.
We create the first matrix3D (object in its initial state):
getObjectValues('objID', 0); // 0 for matrix1
The function
function getObjectValues(div, n){ // 0 for matrix1, 1 for matrix2
let element = document.getElementById(div);
let myTransform = window.getComputedStyle(element,null)
let matrix = myTransform.getPropertyValue("-webkit-transform");
if (matrix === 'none' || typeof matrix === 'undefined') {
// native HTML objects (as divs) are not in a 3D dimension space
// if necessary we create its 3D environment
element.style.transform = 'translateZ(1px)';
getObjectValues(div, n); // then reload the function
}
else {
matrixObj[n] = matrix;
}
}
// Array matrixObj at this step:
[0] -> matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1)
After playing with the different input type="range" (for position, rotation, ... in XYZ dimension) we create the second matrix3D (final state) by clicking on the "TEST" button, which calls
getObjectValues('objID', 1); // 1 for matrix2
// Array matrixObj at this step:
[0] -> matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1)
[1] -> matrix3d(0.945519, 0, -0.325568, 0, 0, 1, 0, 0, 0.325568, 0, 0.945519, 0, 0, 0, -48, 1)
Now the trick: we create a pseudo CSS sheet called "anim-update"
includeCSS("anim-update");
let styleSheet = document.getElementById("anim-update");
// the function
function includeCSS(css) {
let head = document.getElementsByTagName('head')[0];
let sheet = document.createElement('style');
sheet.setAttribute('id',css);
sheet.setAttribute('rel', 'stylesheet');
sheet.setAttribute('type', 'text/css');
head.appendChild(sheet);
}
Once its done we play these 3 sequences:
let delay = 2; // duration
// 1. returns the object to its initial state
setTimeout(function(){
document.getElementById(div).style.transform = matrixObj[0];
},100);
// 2. play the scenario
setTimeout(function(){
styleSheet.innerHTML = '#keyframes anim {
from { transform: '+matrixObj[n][0]+' }
to { transform: '+matrixObj[n][1]+' }
}';
document.getElementById(div).style.animation = 'anim '+delay+'s linear';
},500);
// 3. removes the temporary CSS at the end of the animation
setTimeout(function(){
document.getElementById('anim-update').outerHTML = "";
delete document.getElementById('anim-update');
},500+2000); // +2 seconds -> delay*1000
In this way User can change the final state of the object as much as he wants until he gets the desired animation, which he checks by clicking on the "TEST" button. A "SAVE" button retrieves the data from matrixObj and saves the 2 matrix values. Then move to the next object.
My problem is that when creating faces for a country, there are faces which overlap the border.
Below is the image when all coordinates are used when creating the faces
image with all values (coordinates)
values_axis1 contains all the X coordinates [63.0613691022453, 65.1611029239906, 66.0721609548093, 68.8109022381195, 71.1822098033678..]
values_axis2 contains all the Y coordinates [34.37065245791981, 35.30003249470688, 38.11207931425505, 38.228254752215044..]
values_axis3 contains all the Z coordinates
for (var i = 0; i < values_axis1.length; i ++) {
object_geometry.vertices.push(new THREE.Vector3(values_axis1[i], values_axis2[i], values_axis3[i]));
object_geometry.faces.push(new THREE.Face3(0, i + 1, i));
}
Buy changing i++ to i+=2 we get this image of skipping some values
Arrow obviously shows the point zero where all triangles are drawn from.
Below is the part of code for that.
object_geometry.faces.push(new THREE.Face3(DRAW_FROM_HERE, i + 1, i));
This isn't per say a programming problem but more like a 'is there a algorithm for this'.
I could check for faces that collide with a border from point zero and skip them but then there would be holes and that would need to be filled by drawing from some other location. That could be done manually but doing that for all the countries in the world would take ages. I'm sure this could be done by somehow calculating where the holes are but I have no idea how that would be done.
If someone comes up even with a really bad solution performance wise it would be great! I'm planning on putting all the results in a file which could then be loaded and drawn since there is really no need to do this processing every time since the borders never move.
EDIT: ShapeGeometry was suggested and has been tested. I was wrong when I said that Z coordinates are not relevant but obviously they are due to the curvature of the globe.
Image of the shapeGeometry. 2d but otherwise perfect.
Question edited.
Edit: Solution
Thanks to #Gilles-Philippe Paillé for suggesting ear clipping. Found a great library https://github.com/mapbox/earcut
Here is updated code for others who might have the same issue.
function createVertexForEachPoint(object_geometry, values_axis1, values_axis2,
values_axis3) {
var values_x_y = []; // add x and y values to the same list [x,y,x,y,x,y..]
for (var i = 0; i < values_axis1.length; i++) {
values_x_y.push(values_axis1[i], values_axis2[i]);
}
// https://github.com/mapbox/earcut
var triangles = earcut(values_x_y);
// triangles = [37, 36, 35, 35, 34, 33, 32, 31, 30, 30, …] [a,b,c,a,b,c,a,b,c..]
// triangles contain only the verticies that are needed
// previously all possible faces were added which resulted in overlapping borders
// add all points, in this case coordinates. Same as before
for (var i = 0; i < values_axis1.length; i++) {
object_geometry.vertices.push(new THREE.Vector3(values_axis1[i], values_axis2[i], values_axis3[i]));
}
// go through the list and pick the corners (a,b,c) of the triangles and add them to faces
for (var i = 0; i < triangles.length; i += 3) {
var point_1 = triangles[i];
var point_2 = triangles[i+1];
var point_3 = triangles[i+2];
object_geometry.faces.push(new THREE.Face3(point_1, point_2, point_3));
}
}
I use a geoJSON which has 'polygon' and 'multipolygon' shapes. This code works for polygons atm but shouldn't need too much tweaking to work with multipolygons since the library supports also holes earcut(vertices[, holes, dimensions = 2]).
image of the result
How to effectively create curveVertex with an array of p5.Vector points which fades (decreases opacity) the trail after the mouse position?
So far the following code removes the trailing points after a certain amount of time, creating the effect of trailing after the mouse, but it does not create a translucent trail. The trailing stroke just goes away.
const trail = sk => {
let points = [];
let fadeTime = 1000;
sk.setup = () => {
sk.createCanvas(300, 600);
sk.noFill();
};
sk.draw = () => {
sk.clear();
sk.beginShape();
for (let i = points.length - 1; i >= 0; i--) {
let p = points[i];
let timeAlive = sk.millis() - p.z;
if (timeAlive > fadeTime) {
// start removing last point
points.shift();
} else {
sk.strokeWeight(10);
sk.strokeJoin(sk.ROUND);
sk.strokeCap(sk.ROUND);
sk.stroke(255, 255, 255);
sk.curveVertex(p.x, p.y);
}
}
sk.endShape();
}
// Add more points forward when dragging mouse
sk.touchMoved = () => {
points.push(sk.createVector(sk.mouseX, sk.mouseY, sk.millis()));
return false;
}
};
let mySketch = new p5(trail, elementId);
The problem is that shapes created using the beginShape() and vertex functions (including curveVertex()) can only have a single stroke and fill color. So you can't color parts of the shape differently from the rest of the shape.
To prove this, try changing this line:
sk.stroke(255, 255, 255);
To this line:
sk.stroke(random(256));
To get around this, you could make each section of your curve its own shape with its own color. Start by just making each section a random color.
Then you need to base the color of each section off of the current index. I recommend drawing out a few examples. If a curve has 10 sections, what color should section 1 have? Section 2? Section 10? Repeat that process until you notice a pattern.
If you get stuck, please post an updated MCVE in a new question, and we'll go from there. Good luck.
Sorry if the title is a little confusing, I didn't know what the best way to word it would be.
I'm working on a tile based java-script canvas game that uses sprite sheets and tile maps to create the world and objects in it.
I wrote a section of code to animate a coin to spin around. For a single coin this works fine, but adding more than one coin to the canvas will cause the animation to speed up beyond what's desirable.
Since the game will be adding coins as it progresses, after about 10 coins you probably won't be able to see the animation anymore.
Gif to show the issue:
I've tried multiple methods, even adding a frame delay to slow down the animation, but without the desired results.
Everything is on codepen http://codepen.io/TryHardHusky/pen/EjJdoK
But it's a little messy.
Code I'm using to animate the coin:
var coin = {
height: 32,
width: 32,
cFrame: 0,
mFrame: 8,
image: new Image(),
src: "http://s1.tryhardhusky.com/coin_gold.png",
draw: function(x, y){
coin.cFrame++;
coin.image.src = coin.src;
if(coin.cFrame >= coin.mFrame){
coin.cFrame = 0;
}
ctx.drawImage(coin.image, 32*coin.cFrame,0,32,32,x,y, coin.height,coin.width);
}
}
And to create a coin on the scene:
coin.draw(250,250);
coin.draw(218, 250);
coin.draw(186, 250);
This is a remake of my other pen: http://codepen.io/TryHardHusky/pen/rVbdmw
I had it working there, but was using another inefficient method to animate the coins. It's also very poorly optimized, Hence the reason for the new code.
Can anyone shine some light on what I'm doing wrong?
-- Edit --
Thanks to #canvas was able to fix it with:
var coins = [
[4,5,0],
[2,3,0],
[1,6,0]
];
.
for(var i = 0; i < coins.length; i++){
drawCoin(coins[i], i);
}
.
function drawCoin(cord,i){
coins[i][2] < 8 ? coins[i][2]+=1 : coins[i][2]=0;
var image = new Image();
image.src = "http://s1.tryhardhusky.com/coin_gold.png";
ctx.drawImage(image, 32*cord[2], 0, 32, 32, cord[0]*32, cord[1]*32, 32, 32);
}
Have you tried using this instead of coin?
draw: function(x, y){
this.cFrame++;
this.image.src = this.src;
if(this.cFrame >= this.mFrame){
this.cFrame = 0;
}
ctx.drawImage(this.image, 32*this.cFrame,0,32,32,x,y, this.height,this.width);
Also what you should probably do is have an array of coins, then simply add a new coin to that array then use a loop to draw out each coin and update each coin.
Create an array of coins (example code)
var coins[];
coins.push(new coin(xPosition, yPosition));
// Render coins
for(var i = 0; i < coins.length; i++)
{
coins[i].Draw();
}
I just forked your codepen,
This isn't perfect, but something like this (created an array, updated coin var to be a constructor and then added 3 coins to the new array)
CodePen : http://codepen.io/anon/pen/GJLwJw
me again with a very specific question about Raphael.js . What I would like to do is, that rectangles push against each other. Lets say I have following situation that each rectangle is side by side example picture and I let the one in the middle transform big:
r.click(function() { r.animate({ width: 100, height: 100 }, 500); });
how can I handle it, that the other ones move correctly away like the transformation.
I've tried it by transforming every by hand, but the problem is, my markup isnt that simple.
Thanks in advance.
Its difficult to know a specific answer without seeing any code and knowing what the markup issues or or how you can tie some maths into calculating the figures, it will likely be more complicated, but you may be able to calculate the amount moved by the rects compared to the new width of the original rect. This shows an example of one way you could maybe approach it... jsfiddle here http://jsfiddle.net/R8avm/3/
Edit: There's an updated fiddle which has some basic maths which can sort of figure the end x,y if you know which direction the cubes will be in. http://jsfiddle.net/R8avm/4/ It will need more complex stuff for more rects moving at different angles if thats needed.
<div id="container"></div>
paper = Raphael( "container",400,400);
var rects = [ [ 100,50,50,50, 150,50 ], //x,y,w,h,newx,newy
[ 50,100,50,50, 50,150 ],
[ 50,0,50,50, 50, -50 ],
[ 0,50,50,50, -50,50 ],
];
var newRects = new Array(4);
var r = paper.rect(50,50,50,50).attr({ fill: '#123' });
for (var a=0; a<rects.length; a++) {
newRects[a] = paper.rect( rects[a][0], rects[a][1], rects[a][2], rects[a][3]);
};
r.click(function() {
r.animate({ width: 150, height: 150, x: 0, y: 0 }, 500);
for (var a=0; a<rects.length; a++) {
newRects[a].animate({ x: rects[a][4] , y: rects[a][5] }, 500);
};
});