I create demo game Tic Tac Toe with OOP javascript. But I have problem with get value at table which have attached value then display the table in console.log();
This is my code:
/**
* #constructor
* #param {Number} width - dimension width for table
* #param {Number} height - dimension height for table
*/
function Table(width, height) {
this.table = new Array(height * width);
this.width = width;
this.height = height;
}
Table.prototype = {
/**
* Representation for get width of table
*/
getWidth: function () {
return this.width;
},
/**
* Representation for get height of table
*/
getHeight: function () {
return this.height;
},
/**
* Representation for get table array 2d
*/
getTable: function () {
var x = new Array(this.getHeight());
for (var i = 0; i < this.getHeight(); i++) {
x[i] = new Array(this.getWidth());
};
},
/**
* Representation for set position of table
*/
setPosition: function (x, y, ch) {
return this.table[x][y] = ch;
},
I have problem it here. I can't get value at table and check isEmpty.
/**
* Representation for get value detail at cell of table
*/
getValueAt: function (x, y) {
return this.table[x][y];
},
/**
* Representation for check empty conditional
*/
isEmptyAt: function (x, y) {
if (!this.table[x][y])
return true;
},
};
/**
* #constructor
* #param {String} character - X or O
*/
function Player(name, ch) {
this.name = name;
this.ch = ch;
}
var Printer = function () {
};
This is function print display in console.log().
Printer.prototype = {
/**
* Representation print table
*/
printTable: function (table) {
var str = '';
for (var i = 0; i < table.width; i++) {
var x = i;
for (var j = 0; j < table.height; j++) {
var y = j;
str += '' + table.getValueAt(x, y) + '|';
}
str += '\n------------\n';
}
console.log(str);
},
/**
* Representation check winner conditional
*/
printWinner: function (player) {
},
};
Math.floor(Math.random() * 2);
/**
* #param newTable [array] : The array two-direction table
* #param player [object] : the object contain player X and O
*/
var GamePlay = function (table, playerOne, playerTwo) {
this.table = table;
this.playerOne = playerOne;
this.playerTwo = playerTwo;
this.printer = new Printer();
};
GamePlay.prototype = {
run: function (x, y) {
console.log('Game start ...!');
x = Math.floor(Math.random() * 2);
y = Math.floor(Math.random() * 2);
this.putChessman(x, y, this.playerOne.ch);
console.log('put', this.putChessman());
this.printer.printTable(this.table);
},
/**
* #param player [keywork] : the keywork X and O
*/
checkWin: function (player) {
},
putChessman: function (x, y, ch) {
console.log('isEmptyAt', table.isEmptyAt(x, y));
if (this.table.isEmptyAt(x, y) === true) {
console.log('# player ' + ch + ' put');
this.table.setPosition(x, y, ch);
} else {
console.log('# Other player already put on it');
}
},
};
var table = new Table(3, 3);
var playerOne = new Player('playerOne', 'O');
var playerTwo = new Player('playerTwo', 'X');
var game = new GamePlay(table, playerOne, playerTwo);
game.run();
Please help me resolve problem -.-
You're not initializing table correctly. You're doing this:
this.table = new Array(height * width);
...but later trying to use it like this:
this.table[x][y];
To use it like that, you need not just an array, but an array of arrays (JavaScript's equivalent of a two-dimensional array). To initialize an array of arrays, you do this:
this.table = [];
for (var x = 0; x < width; ++x) {
this.table[x] = new Array(y);
}
Note that the entries in the sub-arrays (e.g., this.table[0][0]) will be undefined*. If that's what you want (it would work with isEmptyAt), that's fine. If you want them to have a different value, you need to fill that in:
this.table = [];
for (var x = 0; x < width; ++x) {
this.table[x] = [];
for (var y = 0; y < height; ++y) {
this.table[x][y] = theValueGoesHere;
}
}
Separately: Calling isEmptyAt will result in either true or undefined, because isEmptyAt only returns a value when it returns true; in the other case, it doesn't return anything, and the result of calling it is the value undefined. Instead, I would have it explicitly return something in both cases:
isEmptyAt: function(x, y) {
return !this.table[x][y];
}
* Technically, with new Array(height), the entries won't be there at all; despite the array having a length of height, it has no entries at all until you add them. But when you try to retrieve an entry, you'll get the value undefined, so I fudged the explanation a bit for simplicity.
Related
I'm trying to implement some technical indicators series and add them to the indicators popop from stock tools. If I import highcharts/indicators/indicators-all I end up getting dozens of indicators, so I figured to import only the ones I need, so far I wasn't able to achieve that, if I import highcharts/indicators/indicators I end up getting only SMA, I tried to import other technical indicators via highcharts/indicators/indicators-INDICATOR-NAME but it didn't work.
Besides that I'd like to create a technical indicator/function such as Linear Regression (from this example) and attach them to the indicators popup as well.
function getLinearRegression(xData, yData) {
var sumX = 0,
sumY = 0,
sumXY = 0,
sumX2 = 0,
linearData = [],
linearXData = [],
linearYData = [],
n = xData.length,
alpha,
beta,
i,
x,
y;
// Get sums:
for (i = 0; i < n; i++) {
x = xData[i];
y = yData[i];
sumX += x;
sumY += y;
sumXY += x * y;
sumX2 += x * x;
}
// Get slope and offset:
alpha = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
if (isNaN(alpha)) {
alpha = 0;
}
beta = (sumY - alpha * sumX) / n;
// Calculate linear regression:
for (i = 0; i < n; i++) {
x = xData[i];
y = alpha * x + beta;
// Prepare arrays required for getValues() method
linearData[i] = [x, y];
linearXData[i] = x;
linearYData[i] = y;
}
return {
xData: linearXData,
yData: linearYData,
values: linearData
};
}
Is that even possible?
Live Demo
EDIT
To add a specific technical indicator you should add as an import highcharts/indicators/NAME (highcharts/indicators/ema,
highcharts/indicators/rsi e.g.)
That feature is not implemented in stock tools, but it could be very useful so you can create a new feature request here: https://github.com/highcharts/highcharts/issues/new/choose
Workaround:
All indicator series from plot options are added to stock tools, so you can customize chart.options.plotOptions, for example in load event:
chart: {
events: {
load: function() {
var plotOptions = this.options.plotOptions,
filteredSeries = {};
Highcharts.objectEach(plotOptions, function(option, key) {
if (!option.params || key === 'dema' || key === 'customlinearregression') {
filteredSeries[key] = option;
}
});
this.options.plotOptions = filteredSeries;
}
}
}
Live demo: https://jsfiddle.net/BlackLabel/xwec9hr7/2/
Useful example: https://www.highcharts.com/stock/demo/stock-tools-custom-gui
Code reference: https://github.com/highcharts/highcharts/blob/371424be0b168de96aa6a58b81ce0b2b7f40d5c5/ts/annotations/popup.ts#L783
In my project, I want my array to increase in size every 5 seconds. I tried to use setInterval to call a function to do this, but it resets my array every 5 seconds with an increased amount rather than naturally growing. Is there a way to increase the amount without having to reset the array each time?
These are the functions I am using to call my "plants":
var myPlants = new Array();
var plantSpawn = 0;
function createPlants() {
reproducePlants();
setInterval(reproducePlants, 5000);
}
function reproducePlants() {
plantSpawn += 5;
for(var i=0; i<plantSpawn; i++){
var rr = Math.round(Math.random() * 150);
var gg = Math.round(Math.random() * 255);
var bb = Math.round(Math.random() * 150);
var plant = new Object();
plant.x = Math.random() * canvas.width;
plant.y = Math.random() * canvas.height;
plant.rad = 2;
plant.skin = 'rgba('+rr+','+gg+','+bb+', 1)';
myPlants[i] = plant;
}
}
You are explicitly reseting all the values of the array when you do this:
for(var i=0; i < plantSpawn; i++){... myPlants[i] = plant; ...}
Note that plantSpawn will hold the new array size, so you are looping over all the old indexes plus the new ones and re-assigning the values on they.
So, instead you can add 5 new elements to the array with Array.push() this way:
var myPlants = new Array();
var plantsInc = 5;
function createPlants()
{
reproducePlants();
setInterval(reproducePlants, 5000);
}
function reproducePlants()
{
// Push N new plants on the array.
for (var i = 0; i < plantsInc; i++)
{
var rr = Math.round(Math.random() * 150);
var gg = Math.round(Math.random() * 255);
var bb = Math.round(Math.random() * 150);
var plant = new Object();
plant.x = Math.random() * canvas.width;
plant.y = Math.random() * canvas.height;
plant.rad = 2;
plant.skin = 'rgba('+rr+','+gg+','+bb+', 1)';
// Push a new plant on the array.
myPlants.push(plant);
}
}
And as a suggestion, you can even wrap the logic to create a new plant inside a method, like this:
var myPlants = new Array();
var plantsInc = 5;
function createPlants()
{
reproducePlants();
setInterval(reproducePlants, 5000);
}
function createPlant()
{
var rr = Math.round(Math.random() * 150);
var gg = Math.round(Math.random() * 255);
var bb = Math.round(Math.random() * 150);
var plant = new Object();
plant.x = Math.random() * canvas.width;
plant.y = Math.random() * canvas.height;
plant.rad = 2;
plant.skin = 'rgba('+rr+','+gg+','+bb+', 1)';
return plant;
}
function reproducePlants()
{
// Push N new plants on the array.
for (var i = 0; i < plantsInc; i++)
{
myPlants.push(createPlant());
}
}
You are overriding all the existing values so instead of using myPlants[i] = plant; use myPlants.push(plant)
You'll want to modify your function to add a plant onto an existing array, rather than looping over your array and assigning it new values.
if you want to add a new value to your array every 5 seconds, something like this should work:
var myPlants = new Array();
var plantSpawn = 0;
function createPlants() {
reproducePlants();
setInterval(reproducePlants, 5000);
}
function reproducePlants() {
var rr = Math.round(Math.random() * 150);
var gg = Math.round(Math.random() * 255);
var bb = Math.round(Math.random() * 150);
var plant = new Object();
plant.x = Math.random() * canvas.width;
plant.y = Math.random() * canvas.height;
plant.rad = 2;
plant.skin = 'rgba('+rr+','+gg+','+bb+', 1)';
myPlants.push(plant);
}
This just adds a new plant to the end of your array rather than assigning new values to your current array each time you call your function. From here you should be able to put that into a for loop if you wanted to add more plants than 1 at a time. each iteration of the loop will only add a single plan to your array.
I have a database that has got a month full of datasets in 10min intervals. (So a dataset for every 10min)
Now I want to show that data on three graphs: last 24 hours, last 7 days and last 30 days.
The data looks like this:
{ "data" : 278, "date" : ISODate("2016-08-31T01:51:05.315Z") }
{ "data" : 627, "date" : ISODate("2016-08-31T01:51:06.361Z") }
{ "data" : 146, "date" : ISODate("2016-08-31T01:51:07.938Z") }
// etc
For the 24h graph I simply output the data for the last 24h, that's easy.
For the other graphs I thin the data:
const data = {}; //data from database
let newData = [];
const interval = 7; //for 7 days the interval is 7, for 30 days it's 30
for( let i = 0; i < data.length; i += interval ) {
newData.push( data[ i ] );
};
This works fine but extreme events where data is 0 or differs greatly from the other values average, can be lost depending on what time you search the data. Not thinning out the data however will result in a large sum of data points that are sent over the pipe and have to be processed on the front end. I'd like to avoid that.
Now to my question
How can I reduce the data for a 7 day period while keeping extremes in it? What's the most efficient way here?
Additions:
In essence I think I'm trying to simplify a graph to reduce points but keep the overall shape. (If you look at it from a pure image perspective)
Something like an implementation of Douglas–Peucker algorithm in node?
As you mention in the comments, the Ramer-Douglas-Peucker (RDP) algorithm is used to process data points in 2D figures but you want to use it for graph data where X values are fixed. I modified this Javascript implementation of the algorithm provided by M Oehm to consider only the vertical (Y) distance in the calculations.
On the other hand, data smoothing is often suggested to reduce the number of data points in a graph (see this post by csgillespie).
In order to compare the two methods, I made a small test program. The Reset button creates new test data. An algorithm can be selected and applied to obtain a reduced number of points, separated by the specified interval. In the case of the RDP algorithm however, the resulting points are not evenly spaced. To get the same number of points as for the specified interval, I run the calculations iteratively, adjusting the espilon value each time until the correct number of points is reached.
From my tests, the RDP algorithm gives much better results. The only downside is that the spacing between points varies. I don't think that this can be avoided, given that we want to keep the extreme points which are not evenly distributed in the original data.
Here is the code snippet, which is better seen in Full Page mode:
var svgns = 'http://www.w3.org/2000/svg';
var graph = document.getElementById('graph1');
var grpRawData = document.getElementById('grpRawData');
var grpCalculatedData = document.getElementById('grpCalculatedData');
var btnReset = document.getElementById('btnReset');
var cmbMethod = document.getElementById('cmbMethod');
var btnAddCalculated = document.getElementById('btnAddCalculated');
var btnClearCalculated = document.getElementById('btnClearCalculated');
var data = [];
var calculatedCount = 0;
var colors = ['black', 'red', 'green', 'blue', 'orange', 'purple'];
var getPeriod = function () {
return parseInt(document.getElementById('txtPeriod').value, 10);
};
var clearGroup = function (grp) {
while (grp.lastChild) {
grp.removeChild(grp.lastChild);
}
};
var showPoints = function (grp, pts, markerSize, color) {
var i, point;
for (i = 0; i < pts.length; i++) {
point = pts[i];
var marker = document.createElementNS(svgns, 'circle');
marker.setAttributeNS(null, 'cx', point.x);
marker.setAttributeNS(null, 'cy', point.y);
marker.setAttributeNS(null, 'r', markerSize);
marker.setAttributeNS(null, 'fill', color);
grp.appendChild(marker);
}
};
// Create and display test data
var showRawData = function () {
var i, x, y;
var r = 0;
data = [];
for (i = 1; i < 500; i++) {
x = i;
r += 15.0 * (Math.random() * Math.random() - 0.25);
y = 150 + 30 * Math.sin(x / 200) * Math.sin((x - 37) / 61) + 2 * Math.sin((x - 7) / 11) + r;
data.push({ x: x, y: y });
}
showPoints(grpRawData, data, 1, '#888');
};
// Gaussian kernel smoother
var createGaussianKernelData = function () {
var i, x, y;
var r = 0;
var result = [];
var period = getPeriod();
for (i = Math.floor(period / 2) ; i < data.length; i += period) {
x = data[i].x;
y = gaussianKernel(i);
result.push({ x: x, y: y });
}
return result;
};
var gaussianKernel = function (index) {
var halfRange = Math.floor(getPeriod() / 2);
var distance, factor;
var totalValue = 0;
var totalFactor = 0;
for (i = index - halfRange; i <= index + halfRange; i++) {
if (0 <= i && i < data.length) {
distance = Math.abs(i - index);
factor = Math.exp(-Math.pow(distance, 2));
totalFactor += factor;
totalValue += data[i].y * factor;
}
}
return totalValue / totalFactor;
};
// Ramer-Douglas-Peucker algorithm
var ramerDouglasPeuckerRecursive = function (pts, first, last, eps) {
if (first >= last - 1) {
return [pts[first]];
}
var slope = (pts[last].y - pts[first].y) / (pts[last].x - pts[first].x);
var x0 = pts[first].x;
var y0 = pts[first].y;
var iMax = first;
var max = -1;
var p, dy;
// Calculate vertical distance
for (var i = first + 1; i < last; i++) {
p = pts[i];
y = y0 + slope * (p.x - x0);
dy = Math.abs(p.y - y);
if (dy > max) {
max = dy;
iMax = i;
}
}
if (max < eps) {
return [pts[first]];
}
var p1 = ramerDouglasPeuckerRecursive(pts, first, iMax, eps);
var p2 = ramerDouglasPeuckerRecursive(pts, iMax, last, eps);
return p1.concat(p2);
}
var internalRamerDouglasPeucker = function (pts, eps) {
var p = ramerDouglasPeuckerRecursive(data, 0, pts.length - 1, eps);
return p.concat([pts[pts.length - 1]]);
}
var createRamerDouglasPeuckerData = function () {
var finalPointCount = Math.round(data.length / getPeriod());
var epsilon = getPeriod();
var pts = internalRamerDouglasPeucker(data, epsilon);
var iteration = 0;
// Iterate until the correct number of points is obtained
while (pts.length != finalPointCount && iteration++ < 20) {
epsilon *= Math.sqrt(pts.length / finalPointCount);
pts = internalRamerDouglasPeucker(data, epsilon);
}
return pts;
};
// Event handlers
btnReset.addEventListener('click', function () {
calculatedCount = 0;
clearGroup(grpRawData);
clearGroup(grpCalculatedData);
showRawData();
});
btnClearCalculated.addEventListener('click', function () {
calculatedCount = 0;
clearGroup(grpCalculatedData);
});
btnAddCalculated.addEventListener('click', function () {
switch (cmbMethod.value) {
case "Gaussian":
showPoints(grpCalculatedData, createGaussianKernelData(), 2, colors[calculatedCount++]);
break;
case "RDP":
showPoints(grpCalculatedData, createRamerDouglasPeuckerData(), 2, colors[calculatedCount++]);
return;
}
});
showRawData();
div
{
margin-bottom: 6px;
}
<div>
<button id="btnReset">Reset</button>
<select id="cmbMethod">
<option value="RDP">Ramer-Douglas-Peucker</option>
<option value="Gaussian">Gaussian kernel</option>
</select>
<label for="txtPeriod">Interval: </label>
<input id="txtPeriod" type="text" style="width: 36px;" value="7" />
</div>
<div>
<button id="btnAddCalculated">Add calculated points</button>
<button id="btnClearCalculated">Clear calculated points</button>
</div>
<svg id="svg1" width="765" height="450" viewBox="0 0 510 300">
<g id="graph1" transform="translate(0,300) scale(1,-1)">
<rect width="500" height="300" stroke="black" fill="#eee"></rect>
<g id="grpRawData"></g>
<g id="grpCalculatedData"></g>
</g>
</svg>
I'm currently learning more about the inner workings of javascript objects and came across an example where multiple variables were returned from the object:
var squareGrid = function(width, n, z) {
width = width || 1;
n = n || 4;
var z = z || width / 2.0;
var edges = [],
pts = [];
var step = width / n;
for (var u = 0; u < n; u++) {
var u_added = u * n * 4;
for (var v = 0; v < n; v++) {
var delta = u_added + 4 * v;
var t_v = step * (v - n / 2);
var t_u = step * (u - n / 2);
pts.push([t_v, t_u, z]); // top left
pts.push([t_v, t_u + step, z]); // top right
pts.push([t_v + step, t_u + step, z]); // bottom right
pts.push([t_v + step, t_u, z]); // bottom left
edges.push([delta + 0, delta + 1]);
edges.push([delta + 1, delta + 2]);
edges.push([delta + 2, delta + 3]);
edges.push([delta + 3, delta + 0]);
}
}
return {
edges: edges,
pts: pts
};
}
In this case, in order to return both edges and points, it looks like there are key-value pairs being returned where the keys and values are the same thing. Is this method necessary or can the following just be done?
return{ edges, pts}};
You would be returning an object where the keys would be edges and the values would be the return value of edges.
x = squareGrid(400, 2, 5)
x.edges
// edges
x.pts
//pts
You could do something like: data = callfunction() -> return [edges, points]
data[0]
// edges
data[1]
//points
** Correction **
when keys are stored in a js hash it serializes the key before attempting to store the value
what would get stored is the serialized version of the array as the key, with the value being the array
I am rewriting this question because my first one was quite vague. I am trying to conver the following javascript function using the map function into Swift 2.
Here is the javascript function.
function compute_correlations(timeseries, test_frequencies, sample_rate)
{
// 2pi * frequency gives the appropriate period to sine.
// timeseries index / sample_rate gives the appropriate time coordinate.
var scale_factor = 2 * Math.PI / sample_rate;
var amplitudes = test_frequencies.map
(
function(f)
{
var frequency = f.frequency;
// Represent a complex number as a length-2 array [ real, imaginary ].
var accumulator = [ 0, 0 ];
for (var t = 0; t < timeseries.length; t++)
{
accumulator[0] += timeseries[t] * Math.cos(scale_factor * frequency * t);
accumulator[1] += timeseries[t] * Math.sin(scale_factor * frequency * t);
}
return accumulator;
}
);
return amplitudes;
}
And here is my Swift function. I am getting an error and am not even sure I am doing it correctly. Error is noted in the code.
func compute_correlations(timeseries:[Double], test_frequencies:[NoteInfo], sample_rate:Double) -> [Double]
{
// 2pi * frequency gives the appropriate period to sine.
// timeseries index / sample_rate gives the appropriate time coordinate.
let scale_factor = 2 * pi / sample_rate;
let amplitudes: [Double] = test_frequencies.map { f in
let frequency = f.getFrequency()
// Represent a complex number as a length-2 array [ real, imaginary ].
var accumulator: [Double] = [ 0.0, 0.0 ]
for (var t = 0; t < timeseries.count; t++)
{
accumulator[0] += timeseries[t] * cos(scale_factor * frequency * Double(t))
accumulator[1] += timeseries[t] * sin(scale_factor * frequency * Double(t))
}
return accumulator //ERROR Cannot convert return expression of type '[Double]' to return type 'Double'
}
return amplitudes;
}
And if needed here is the NoteInfo class
class NoteInfo {
var frequency:Double!
var note_name:String!
init(theFrequency:Double, theNoteName:String){
frequency = theFrequency
note_name = theNoteName
}
func getFrequency()-> Double {
return frequency
}
func getNoteName()-> String {
return note_name
}
}
Here is where I am populating the test_frequencies
for (var i = 0; i < 30; i++)
{
let note_frequency = C2 * pow(2.0, Double(i) / 12.0)
let note_name = notes[i % 12]
let note = NoteInfo(theFrequency: note_frequency, theNoteName: note_name)
test_frequencies.append(note)
}
Your accumulator is a [Double], and so the result of your map becomes [[Double]]. You then try to assign it to a [Double].
You should either declare amplitudes accordingly:
let amplitudes: [[Double]] = test_frequencies.map { f in
or (depending on your needs) return only one of the accumulator fields inside your map, e.g.
return accumulator[0]