Tic Tac Toe using Javascript - javascript

I have created a Tic Tac Toe game using Javascript. Live demo can be seen here
The code works fine but it has a little bug. When I click on the canvas, the image is drawn and everything works fine. But as I click on canvas, I invoke the superclick() function which takes random path that hasn't been clicked on yet and draws set picture (O or X) on set position.
function superclick() {
var filt = paths.filter(function(x) {
return x.avaible == true;
});
var rand = filt[getRandomIntInclusive(0, paths.length - 1)];
/*var evt = new MouseEvent("click", {
view: window,
bubbles: true,
cancelable: true,
clientX: (canvas.offsetLeft + rand.x)+5,
clientY: (canvas.offsetTop + rand.y)+5
});
canvas.dispatchEvent(evt)*/
ctx.drawImage(turn[turns],rand.x,+ rand.y);
score[turns] += rand.id;
rand.avaible = false;
turns = turns == "x" ? "o" : "x";
}
The problem here is that sometimes this function is invoked, and sometimes it isn't. When it isn't invoked it throws an error "Cannot read property 'x' of undefined", andI can't understand why. When I try to console.log set random path x it works. Did I miss something?

You're getting a random number between 0 and paths.length (so 0 and 8), but youre using that index to access "filt[]" which is only those squares that haven't been filled in. So you might try to get the 8th element of "filt[]" when only 4 remain.
Try
var rand = filt[getRandomIntInclusive(0, filt.length - 1)];
This will make the max random number you grab proportional to the number of remaining spaces.

Your "filt" variable is an array of object that begins with (at game launch) with 9 items -- one for each square. You are filtering the items as you go based on "avaible" (sic) squares.
Since the length of the paths array does not change (9), getting a random index (via 'getRandomIntInclusive') based on the range 1..9 returns an out of bound error because the length of the filt array is reduced on every turn.
What you want to do is change:
var rand = filt[getRandomIntInclusive(0, paths.length - 1)];
To:
var rand = filt[getRandomIntInclusive(0, filt.length - 1)];
...and you should be in a happy place.

Related

JS exercise issue + loop + arrays + objects

I'm following an exercise's tutorial but I cannot understand the logic.
The initial setup is a 3x3 board made of a 2D array. Each nested array will have 3 elements, and they will be objects with two properties: x and y, defining their position in the array. (x represent the index and y the position of each element in the nested array so it will be 0,1 or 2 since each nested array has 3 elements).
we define an object we will be using: it will be named player and, for now, will have x and y properties set to the following values:
const player = {
x: 1,
y: 1
};
So now the object is placed in the middle of our 3x3 board with position {1,1}
Our next logical step is to start writing some code and to define a function that will allow us, whenever called, depending on the given command (go left or go right) to change the position of the player on the board. To be able to affect the player’s properties and its position, our function has to have access to the player object. Also, we have to set some condition in order not to allow our player to leave the board. And that's the condition I do not understand:
if (thePlayer.y >= 0 && thePlayer.y < 2) {
if (command === 'l') {
thePlayer.y--;
} else {
thePlayer.y++;
}
console.log(`Player has position: x=${thePlayer.x}, y=${thePlayer.y}`);
} else {
console.log("You can't place player outside of the board!");
}
}
Why the first 'if' statement says >=0 and not >0 ?
if the value of y was 0, the player would be on the left side of the board, and then still able to move left because equal to zero and then would go out of the board.
What makes me totally blank is the following sentence:
Now, if our player’s property “y” is lower than 0, we’ll get a notification that the player left the board, and that is not allowed.
The exercise is right but I would like to understand!
This logic is wrong for me also, You will not be able to move 'player' when it will be on Y = 0, which is still correct position. I guess the better and clear solution will be first check the command and then check if command is valid or not, smthg like this:
const moveHandlers = {
'l': () => thePlayer.y--,
'r': () => thePlayer.y++,
}
const validCommands = Object.keys(moveHandlers);
if (!validCommands.includes(command)) {
throw new Error('Invalid command');
}
const invalidLeftMove = command === 'l' && thePlayer.y === 0;
const invalidRightMove = command === 'r' && thePlayer.y === 2;
if (invalidLeftMove || invalidRightMove) {
throw new Error("You can't place player outside of the board!");
}
moveHandlers[command]();

how to return to start when iterating over array and iterator become larger than length (in a graphics render loop)

I'm trying to make an animation recorder that records x,y positions into an array and allow the animation to be recalled. I specifically have p5.js in mind as the graphics lib, but any should work. since this is just array work.
in p5.js to return the value of Sin() or Cos() you can pass them an angle, that angle can be ever incrementing since 2PI == 4PI (in terms of the direction the rotation is facing) etc. I'm looking to replicate this kind of function but to return the data stored in an array.
so for example you've got an array like the following
let demo = ['297', '298', '299', '300']
It would be easy to loop over the array once since it has 4 items, but I'd like to write a function where if we passed in 4, it would return index 0, '297' or if we fed in 11, it would return '300' or if we fed in 22 it would return '299'
this way the function could continually be fed in an ever increasing value that moves up each frame we could return the values of the array in a loop.
let survey = 0;
let demo = ['297', '298', '299', '300']
//a rendering loop
function draw(){
survey ++
let xPos = getPosition(survey) //this getPosition function is the one in question
ellipse(xPos,100,50)
}
I feel like this is some modulo math, but I cant quite get it sorted.
thanks for taking a look!
The solution to your problem is the modulus (%) operator. This operator will return the remainder of the division.
E.g. 11 % 4 = 3
const positions = [297, 298, 299, 300];
function getPosition(positions, i) {
return positions[i % positions.length];
}
console.log(getPosition(positions, 4)); // 297
console.log(getPosition(positions, 11)); // 300
console.log(getPosition(positions, 22)); // 299

Generate new array with .pop() value

I am drawing points on a canvas with javascript, like so:
function addPointToMap(point) {
var pointRadius = (document.getElementById(point.canvasId).height * (2 / 66)) / 2;
var context = document.getElementById(point.canvasId).getContext("2d");
context.beginPath();
context.arc(point.x, point.y, pointRadius, 0, 2 * Math.PI);
context.fillStyle = "red";
context.fill();
}
and all the points I am drawing are stored in an array, pointMap. I want the user to only be able to draw one point if a checkbox is ticked, and draw many points if it is not ticked. A new point should override an old one. In order to do this, I have decided to add the new point to the array, and then remove the old one and refresh the canvas. The problem is that pointMap = pointMap.pop(); is returning an empty array. How do I get the most recent entry in an array and delete all the other entries? Here is what I have so far:
if (questionId == 41) {
if (pointMap.length == 1) {
//do nothing, user only has 1 point
} else {
console.log("PointMap: " + pointMap); //ex. returns [Point, Point] (Point is a custom class I wrote to store the point x and y values)
pointMap = pointMap.pop(); //this line does not work
console.log("PointMap: " + pointMap); //ex. returns []
refreshCanvas();
}
}
Where am I going wrong? can anyone steer me in the right direction?
pop returns the popped value, so pointMap = pointMap.pop() will replace your array reference with a point.
If you want to only have a single point in the array when the checkbox is checked, simply overwrite it:
if (checkboxIsChecked) {
// Only want one point, assign to index 0 (works whether the
// array already has a point or not)
pointMap[0] = theNewPoint;
} else {
// Want to allow multiple points, push the point onto the array
pointMap.push(theNewPoint);
}
If the user can check the checkbox while there are already values in pointMap, you'll want to remove all but the last one when they check it. In your event handler for the checkbox:
if (checkboxIsChecked && pointMap.length > 1) {
// Remove all entries except the last pushed one
pointMap.splice(0, pointMap.length - 1);
}

Change length of cylinder or extrudedHeight of circle

I'm trying to change the length of a cylinder or the extrudedHeight of a circle when it has been added to the primitives and is shown in the cesium widget/viewer. For example this cylinder:
var length = 100;
var cylinderGeometry = new Cesium.CylinderGeometry({
length : length,
topRadius : cylinderradius,
bottomRadius : cylinderradius,
slices: cylinderslices,
vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
});
var cylinder = new Cesium.GeometryInstance({
geometry: cylinderGeometry,
modelMatrix: Cesium.Matrix4.multiplyByTranslation(
Cesium.Transforms.eastNorthUpToFixedFrame(ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(lon, lat))),
new Cesium.Cartesian3(0.0, 0.0, length * 0.5)),
attributes: {
color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)
},
id: "Cylinder1"
});
var primitive = new Cesium.Primitive({
geometryInstances : cylinder ,
appearance : new Cesium.PerInstanceColorAppearance({
closed : false,
translucent: true,
flat: false,
faceForward: true
}),
allow3DOnly: true,
vertexCacheOptimize: true,
allowPicking: true,
releaseGeometryInstances: false
});
widget.scene.primitives.add(primitive);
Because it's added to the primitives array it will be shown in the widget, but after 2 seconds for example I get a notification that the length should be halved (that means set to 50). Is there any way to do this? Simply changing it in cylinderGeometry doesn't seem to do the job.
I kind of have it working by creating a new cylinder with the new height, adding it and removing the old one. This however tends to flicker the cylinder (it's gone for a fraction of a second) before the new one is shown. I fixed this problem by removing the old instance after a set time after the new one is added. This whole solution isn't very elegant and doesn't work very well on devices with a small amount of computing power, hence my search for a better solution.
I don't care if this is achieved using cylinders or extruded circles. If you need any more information don't hesitate to ask in the comments below the question.
EDIT
I implemented the second solution Matthew suggested but after a while of it running perfectly the cylinders stop changing height (which didn't occur when I used my solution. The callback in the interval does get called. Here is some code showing what my new solution is (not working):
primitives.add(prim);
window.nodeValuesInterval = setInterval(function () {
if (prim._state == Cesium.PrimitiveState.COMPLETE) {
clearInterval(window.nodeValuesInterval);
clearTimeout(window.nodeValuesTimeout);
primitives.remove(primitiveObjects.value);
primitiveObjects.value = prim;
}
}, cylindervalueinterval);
window.nodeValuesTimeout = setTimeout(function () {
clearInterval(window.nodeValuesInterval);
primitives.remove(primitiveObjects.value);
primitiveObjects.value = prim;
}, cylindervaluedelay);
Cesium's geometry is currently optimized for static data. Some attributes, such as visibility, color, and material can be changed on the fly, but items that actually modify the geometry (like cylinder height) require you to remove the primitive and recompute the geometry. The flickering your seeing is the result of asynchronous primitive creation being on by default. There are two ways to do want you want.
Disable asynchronous primitive create by passing [options.asynchronous: false to the Primitive constructor. This means that when you add a new primitive, Cesium will not render until it is ready. For one or two objects, you won't notice anything. For lots of objects it will lock up the browser until everything is ready. This does guarantee that you can remove old/add new primitives without any flicker.
The second option is to add your new primitive (without removing the old one) and then every frame, check the _state property of your new Primitive (I thought this was part of the public API but apparently it's not). When the _state is equal to Cesium.PrimitiveState.COMPLETE you can safely remove the old primitive and your guaranteed the new one will render (hence no flicker).
I think we have a bug/feature request to expose the state variable publicly or otherwise notify when the Primitive is ready; but using _state should be fine for the forseeable future. I'll update this issue if we add an official way sometime soon.
Hope that helps.
EDIT: Since more help was requested; here's a complete example. You can copy and paste the below code into Sandcastle using this link.
Basically it uses the scene.preRender event instead of a timeout (preRender is almost always the better answer here). Also, if you receive a new update before the old one is finished processing, it's important to remove that one before computing the new one. Let me know if you are still having problems.
require(['Cesium'], function(Cesium) {
"use strict";
var widget = new Cesium.CesiumWidget('cesiumContainer');
var ellipsoid = widget.scene.globe.ellipsoid;
var lon = 0;
var lat = 0;
var cylinderradius = 30000;
var length = 10000000;
var cylinderslices = 32;
var newPrimitive;
var currentPrimitive;
//This function creates a new cylinder that is half the length of the old one.
function decreaseLength() {
//If there's a pending primitive already, remove it.
if(Cesium.defined(newPrimitive)){
widget.scene.primitives.remove(newPrimitive);
}
length /= 2;
var cylinderGeometry = new Cesium.CylinderGeometry({
length : length,
topRadius : cylinderradius,
bottomRadius : cylinderradius,
slices: cylinderslices,
vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
});
var cylinder = new Cesium.GeometryInstance({
geometry: cylinderGeometry,
modelMatrix: Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame(ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(lon, lat))),
new Cesium.Cartesian3(0.0, 0.0, length * 0.5)),
attributes: {
color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)
},
id: "Cylinder1"
});
newPrimitive = new Cesium.Primitive({
geometryInstances : cylinder ,
appearance : new Cesium.PerInstanceColorAppearance({
closed : false,
translucent: true,
flat: false,
faceForward: true
}),
allow3DOnly: true,
vertexCacheOptimize: true,
allowPicking: true,
releaseGeometryInstances: false
});
//We add the new cylinder immediately, but don't remove the old one yet.
widget.scene.primitives.add(newPrimitive);
}
//Create the initial cylinder.
decreaseLength();
//Subscribe to the preRender event so we can check the primitive every frame.
widget.scene.preRender.addEventListener(function(scene, time) {
//Remove the old cylinder once the new one is ready.
if(Cesium.defined(newPrimitive) && newPrimitive._state === Cesium.PrimitiveState.COMPLETE){
if(Cesium.defined(currentPrimitive)){
widget.scene.primitives.remove(currentPrimitive);
}
currentPrimitive = newPrimitive;
newPrimitive = undefined;
}
});
Sandcastle.addToolbarButton('Decrease Length', decreaseLength);
Sandcastle.finishedLoading();
});

Canvas getImageData() For optimal performance. To pull out all data or one at a time?

I need to scan through every pixel in a canvas image and do some fiddling with the colors etc. For optimal performance, should I grab all the data in one go and work on it through the array? Or should I call each pixel as I work on it.
So basically...
data = context.getImageData(x, y, height, width);
VS
data = context.getImageData(x, y, 1, 1); //in a loop height*width times.
You'll get much higher performances by grabbing the image all at once since :
a) a (contiguous) acces to an array is way faster than a function call.
b) especially when this function isa method of a DOM object having some overhead.
c) and there might be buffer refresh issues that might delay response (if canvas is
on sight... or not depending on double buffering implementation).
So go for a one-time grab.
I'll suggest you look into Javascript Typed Arrays to get the most of the
imageData result.
If i may quote myself, look at how you can handle pixels fast in this old post of mine
(look after 2) ):
Nice ellipse on a canvas?
(i quoted the relevant part below : )
You can get a UInt32Array view on your ImageData with :
var myGetImageData = myTempCanvas.getImageData(0,0,sizeX, sizeY);
var sourceBuffer32 = new Uint32Array(myGetImageData.data.buffer);
then sourceBuffer32[i] contains Red, Green, Blue, and transparency packed into one unsigned 32 bit int. Compare it to 0 to know if pixel is non-black ( != (0,0,0,0) )
OR you can be more precise with a Uint8Array view :
var myGetImageData = myTempCanvas.getImageData(0,0,sizeX, sizeY);
var sourceBuffer8 = new Uint8Array(myGetImageData.data.buffer);
If you deal only with shades of grey, then R=G=B, so watch for
sourceBuffer8[4*i]>Threshold
and you can set the i-th pixel to black in one time using the UInt32Array view :
sourceBuffer32[i]=0xff000000;
set to any color/alpha with :
sourceBuffer32[i]= (A<<24) | (B<<16) | (G<<8) | R ;
or just to any color :
sourceBuffer32[i]= 0xff000000 | (B<<16) | (G<<8) | R ;
(be sure R is rounded).
Listening to #Ken's comment, yes endianness can be an issue when you start fighting with bits 32 at a time.
Most computer are using little-endian, so RGBA becomes ABGR when dealing with them 32bits a once.
Since it is the vast majority of systems, if dealing with 32bit integer assume this is the case,
and you can -for compatibility- reverse your computation before writing the 32 bits results on Big endian systems.
Let me share those two functions :
function isLittleEndian() {
// from TooTallNate / endianness.js. https://gist.github.com/TooTallNate/4750953
var b = new ArrayBuffer(4);
var a = new Uint32Array(b);
var c = new Uint8Array(b);
a[0] = 0xdeadbeef;
if (c[0] == 0xef) { isLittleEndian = function() {return true }; return true; }
if (c[0] == 0xde) { isLittleEndian = function() {return false }; return false; }
throw new Error('unknown endianness');
}
function reverseUint32 (uint32) {
var s32 = new Uint32Array(4);
var s8 = new Uint8Array(s32.buffer);
var t32 = new Uint32Array(4);
var t8 = new Uint8Array(t32.buffer);
reverseUint32 = function (x) {
s32[0] = x;
t8[0] = s8[3];
t8[1] = s8[2];
t8[2] = s8[1];
t8[3] = s8[0];
return t32[0];
}
return reverseUint32(uint32);
};
Additionally to what GameAlchemist said, if you want to get or set all the colors of a pixel simultaneously, but you don't want to check endianness, you can use a DataView:
var data = context.getImageData(0, 0, canvas.width, canvas.height);
var view = new DataView(data.data.buffer);
// Read or set pixel (x,y) as #RRGGBBAA (big endian)
view.getUint32(4 * (x + y*canvas.width));
view.setUint32(4 * (x + y*canvas.width), 0xRRGGBBAA);
// Read or set pixel (x,y) as #AABBGGRR (little endian)
view.getUint32(4 * (x + y*canvas.width), true);
view.setUint32(4 * (x + y*canvas.width), 0xAABBGGRR, true);
// Save changes
ctx.putImageData(data, 0, 0);
It depends on what exactly you're doing, but I'd suggest grabbing it all at once, and then looping through it.
Grabbing it all at once is faster than grabbing it pixel by pixel, since searching through an array is a lot faster than searching through a canvas, once for each pixel.
If you're really in need of speed, look into web workers. You can set each one to grab a specific section of the canvas, and since they can run simultaneously, they'll make much better use out of your CPU.
getImageData() isn't really slow enough for you to notice the difference if you were to grab it all at once or individually, in my experiences using the function.

Categories

Resources