Paint by number Illustrator script - javascript
I am from supercoloring and we decided to convert our vector illustrations in color to color by number worksheets. Our input files are color and outline images in svg format.
Outline version (like a coloring page) + Color version
outline version and
color version
What we want to get is the following
result
We would like that a color palette is generated under the outline version of the image based on the color data from the color version of the image. Moreover, numbers corresponding to this palette are placed inside each color space of the outlined version.
I understand that no script in the world would do this properly, but at least I am striving to reduce the time spent by the editor (person) to put these numbers manually in the Illustrator. I understand that our color vector images may have too many colors and shades so we need somehow to limit the result colors of the palette ( to fuse them into large groups of basic colors).
I searched all over the stackoverflow solutions and found some ingenious like Paint with numbers with Adobe Illustrator Javascript and
I'm looking to create an automated numbering system for custom paint by number kits in photoshop (Kudos to Yuri Khristich). However, they are not exactly adapted to our needs.
Most of scripts on the web generate outlined images from color version, but the quality is compromised. We have already a proper outline version that we want to use as a base for color by number worksheet.
Here is the script to make a 'color palette' for selected artwork.
And here, as you know, is the script to add color names to all filled areas.
So I took the two script, made a couple of minimal tweaks and get almost the result you want. All you need after the scripts is to copy the layer with numbers and 'palette' from a colored artwork to a outline version.
Script #1
// Modified version
// https://stackoverflow.com/questions/75344674/paint-by-number-illustrator-script
// Original:
// https://productivista.com/make-a-list-of-colors-from-your-selection/
/*
Date: July, 2020
Author: Katja Bjerrum, email: katja#productivista.com, www.productivista.com
============================================================================
NOTICE:
This script is provided "as is" without warranty of any kind.
Free to use, not for sale.
============================================================================
Released under the MIT license.
http://opensource.org/licenses/mit-license.php
============================================================================
*/
//#target illustrator
var doc = app.activeDocument;
var myLayer = doc.activeLayer;
app.coordinateSystem = CoordinateSystem.ARTBOARDCOORDINATESYSTEM;
var swGrps = doc.swatchGroups;
var mainSwGr = doc.swatchGroups[0];
var sel = doc.selection;
var actionSet = 'CreateSwatchGroup';
var actionName = 'ColourGroup';
var actionPath = Folder.myDocuments + '/Adobe Scripts/';
if (!Folder(actionPath).exists) Folder(actionPath).create();
//app.doScript("Colorgroup", "ToSwatchScript"); // Action, that creates swatch group
var actionDoc =
[ '/version 3',
'/name [' + actionSet.length + ' ' + ascii2Hex(actionSet) + ']',
'/isOpen 1',
'/actionCount 1',
'/action-1 {',
'/name [' + actionName.length + ' ' + ascii2Hex(actionName) + ']',
' /keyIndex 0',
' /colorIndex 0',
' /isOpen 1',
' /eventCount 1',
' /event-1 {',
' /useRulersIn1stQuadrant 0',
' /internalName (ai_plugin_swatches)',
' /localizedName [ 8',
' 5377617463686573',
' ]',
' /isOpen 0',
' /isOn 1',
' /hasDialog 1',
' /showDialog 1',
' /parameterCount 1',
' /parameter-1 {',
' /key 1835363957',
' /showInPalette 4294967295',
' /type (enumerated)',
' /name [ 15',
' 4e657720436f6c6f722047726f7570',
' ]',
' /value 17',
' }',
' }',
'}'].join('');
createAction(actionDoc, actionName, actionPath);
app.redraw();
app.doScript (actionName, actionSet);
app.redraw();
app.unloadAction(actionSet, '');
var convMM = 2.8346456692; // initialization of the variable to convert points to mm
var colorgroup = doc.swatchGroups[doc.swatchGroups.length - 1]; // Choose the last swatch group
var stY = -200; //
var stX = 20;
var recW = 25;
var recH = 25;
var offX = recW / 5;
var offY = recH / 4;
var textoffY = recH / 4;
var rows = 4;
var cols = 4;
var black = new GrayColor();
black.gray = 80;
var white = new GrayColor() ;
white.gray = 0;
var noStroke = doc.swatches[0].color;
if (swGrps.length <=1){
alert ("Please create swatch group from your selection");
}
else if (sel <= 0){
//docRef.placedItems[0].selected == false;
alert ("Please make a selection");
delSwatchGr(colorgroup); //delete swatch group
}
else{
swatchGroupList(colorgroup, stY, stX);//create corlor list
// delSwatchGr(colorgroup);//delete swatch group
}
//Function, that creates color list
function swatchGroupList(swatchGroup, stY, stX) {
// Groups everything in the list
var mainGroup = myLayer.groupItems.add();
mainGroup.name = "Colors";
mainGroup.moveToBeginning(myLayer);
//Name of the color list
var nameText = myLayer.textFrames.add();
nameText.contents = swatchGroup.name; // the name of the swatch group
nameText.position = [stX, stY + recH];
var nameStyle = nameText.textRange.characterAttributes;
nameStyle.size = 12;//size in punkt
//nameStyle.textFont = textFonts.getByName("Avenir-Book");//the font
nameStyle.capitalization = FontCapsOption.ALLCAPS;//ALL CAPITALS
var swatches = swatchGroup.getAllSwatches();
var swatchArray = [];
for (i = swatches.length-1; i>=0; i--) {
var mySwatch = swatches[i];
mySwatch.name = i + 1;
var subGroup = createSwatchGroup(mySwatch, textoffY);
swatchArray.push(subGroup);
}
nameText.moveToEnd(mainGroup);
var myGroup = swatchArray;
var maxW = maxWidth(myGroup);
for (var j = 0; j < myGroup.length; j++) {
var mySubGroup = myGroup[j];
mySubGroup.moveToBeginning(mainGroup);
}
for (var i = 0; i < mainGroup.groupItems.length; i++) {
var mySubGroup = mainGroup.groupItems[i];
if (mainGroup.groupItems.length > 7) {
rows = 7;
var c = i%rows;
var r = Math.floor(i/rows);
mySubGroup.position = [stX + r * (maxW + 10), stY - c * (recH + offY)];
}
else {
rows = 7;
var c = i % rows;
var r = Math.floor(i / rows);
mySubGroup.position = [stX, stY - c * (recH + offY)];
}
}
// textSwatch.moveToBeginning(SubGroup);
// path_ref.moveToBeginning(SubGroup);
// SubGroup.position = [stX + c * 140, stY - r * (path_ref.height + offY)];
subGroup.moveToBeginning(mainGroup);
}
function lightColor(c){
if(c.typename)
{
switch(c.typename)
{
case "CMYKColor":
return (c.black>=10 || c.cyan>10 || c.magenta>10 || c.yellow > 10) ? true : false;
case "RGBColor":
return (c.red<230 || c.green<230 || c.blue<230) ? true : false;
case "GrayColor":
return c.gray >= 10 ? true : false;
case "SpotColor":
return lightColor(c.spot.color);
//return false;
}
}
}
function fitItem(item, itemW, itemH, diff) {
var oldWidth = item.width
var oldHeight = item.height
if (item.width > item.height) {
// landscape, scale height using ratio from width
item.width = itemW - diff.deltaX
var ratioW = item.width / oldWidth
item.height = oldHeight * ratioW
} else {
// portrait, scale width using ratio from height
item.height = itemH - diff.deltaY
var ratioH = item.height / oldHeight
item.width = oldWidth * ratioH
}
}
function itemBoundsDiff(item) {
var itemVB = item.visibleBounds
var itemVW = itemVB[2] - itemVB[0] // right - left
var itemVH = itemVB[1] - itemVB[3] // top - bottom
var itemGB = item.geometricBounds
var itemGW = itemGB[2] - itemGB[0] // right - left
var itemGH = itemGB[1] - itemGB[3] // top - bottom
var deltaX = itemVW - itemGW
var deltaY = itemVH - itemGH
var diff = { deltaX: deltaX, deltaY: deltaY }
return diff
}
function delSwatchGr(swGr){
var swGrSws = swGr.getAllSwatches();
for (var j = 0; j < swGrSws.length; j++){
var sw = swGrSws[j];
sw.color = new CMYKColor();
}
swGr.remove();
}
//Function finds the max group width
function maxWidth(myGroup) {
var maxFound = 0;
for (var j = 0; j < myGroup.length; j++) {
var GrWidth = myGroup[j].width;
//var Widthmax = GrWidth.width;
maxFound = Math.max(maxFound, GrWidth);
}
return maxFound;
}
function createSwatchGroup(sw, myOffset) {
//Is "MyForm" path exists?
try{
var path_ref_ori = app.activeDocument.pathItems.getByName("MyForm" || "myform" || "MYFORM");
}
catch(e) {
var path_ref_ori = false;
}
if (path_ref_ori) {
myPath = path_ref_ori.duplicate();
var boundsDiff = itemBoundsDiff(myPath);
fitItem(myPath, recW, recH, boundsDiff);
myPath.name = "NewForm";
myPath.position = [0, 0];
}
else {
var myPath = createMyPath()
}
myPath.fillColor = sw.color;
myPath.stroked = true;
myPath.strokeWidth = 0.3;
myPath.strokeColor = lightColor(myPath.fillColor) ? noStroke : black;
var textSwatch = myLayer.textFrames.add(); //swatch text
textSwatch.contents = sw.name;
textSwatch.position = [myPath.width + 1.3 * convMM, -myOffset];
var textSwStyle = textSwatch.textRange.characterAttributes;
textSwStyle.size = 10; //size in punkt
//textSwStyle.textFont = textFonts.getByName("MyriadPro-Semibold"); //the font
var SubGroup = myLayer.groupItems.add(); //groups path and text
SubGroup.name = sw.name;
SubGroup.position = [0, 0];
textSwatch.moveToBeginning(SubGroup);
myPath.moveToBeginning(SubGroup);
return SubGroup;
}
function createMyPath(){
//Is "MyForm" path exists?
try{
var path_ref_ori = app.activeDocument.pathItems.getByName("MyForm" || "myform" || "MYFORM");
}
catch(e) {
var path_ref_ori = false;
}
if (path_ref_ori) {
path_ref = path_ref_ori.duplicate();
var boundsDiff = itemBoundsDiff(path_ref);
fitItem(path_ref, recW, recH, boundsDiff);
path_ref.name = "NewForm";
path_ref.position = [0, 0];
}
else {
var path_ref = myLayer.pathItems.rectangle(0, 0, recW, recH); //swatch path item
}
return path_ref
};
function createAction(str, set, path) {
var f = new File('' + path + '/' + set + '.aia');
f.open('w');
f.write(str);
f.close();
app.loadAction(f);
f.remove();
};
function ascii2Hex(hex) {
return hex.replace(/./g, function (a) { return a.charCodeAt(0).toString(16) });
};
Input (after select the artwork and run the script):
Result (added the global swatches and the 'color palette' at the bottom):
Script #2
// Based on:
// https://stackoverflow.com/questions/73705368/paint-with-numbers-with-adobe-illustrator-javascript
var doc = app.activeDocument,
lays = doc.layers,
WORK_LAY = lays.add(),
NUM_LAY = lays.add(),
i = lays.length - 1,
lay;
// main working loop
for (; i > 1; i--) {
//process each layer
lay = lays[i];
lay.name = lay.name + " Num:" + (i - 1); // i-1 as 2 layers beed added.
process(lay.pathItems, false);
process(lay.compoundPathItems, true); // if any
}
//clean up
NUM_LAY.name = "Numbers";
WORK_LAY.remove();
function process(items, isCompound) {
var j = 0,
b, xy, s, p, op;
for (; j < items.length; j++) {
// process each pathItem
op = items[j];
try { color = op.fillColor.spot.name } catch(e) { continue } // <-- HERE
// add stroke
if (isCompound) {
// strokeComPath(op);
} else {
// !op.closed && op.closed = true;
// op.filled = false;
// op.stroked = true;
};
b = getCenterBounds(op);
xy = [b[0] + (b[2] - b[0]) / 2, b[1] + (b[3] - b[1]) / 2];
s = (
Math.min(op.height, op.width) < 20 ||
(op.area && Math.abs(op.area) < 150)
) ? 20 : 40; // adjust font size for small area paths.
add_nums(color, xy, s); // <--- HERE
}
}
function getMinVisibleSize(b) {
var s = Math.min(b[2] - b[0], b[1] - b[3]);
return Math.abs(s);
}
function getGeometricCenter(p) {
var b = p.geometricBounds;
return [(b[0] + b[2]) / 2, (b[1] + b[3]) / 2];
}
// returns square of distance between p1 and p2
function getDist2(p1, p2) {
return Math.pow(p1[0] + p2[0], 2) + Math.pow(p1[1] + p2[1], 2);
}
// returns visibleBounds of a path in a compoundPath p
// which is closest to center of the original path op
function findBestBounds(op, p) {
var opc = getGeometricCenter(op);
var idx = 0,
d;
var minD = getDist2(opc, getGeometricCenter(p.pathItems[0]));
for (var i = 0, iEnd = p.pathItems.length; i < iEnd; i++) {
d = getDist2(opc, getGeometricCenter(p.pathItems[i]));
if (d < minD) {
minD = d;
idx = i;
}
}
return p.pathItems[idx].visibleBounds;
}
function applyOffset(op, checkBounds) {
var p = op.duplicate(WORK_LAY, ElementPlacement.PLACEATBEGINNING),
// offset value the small the better, but meantime more slow.
offset = function() {
var minsize = Math.min(p.width, p.height);
if (minsize >= 50) {
return '-1'
} else if (20 < minsize && minsize < 50) {
return '-0.5'
} else {
return '-0.2' // 0.2 * 2 (both side ) * 50 (Times) = 20
}
},
xmlstring = '<LiveEffect name="Adobe Offset Path"><Dict data="I jntp 2 R mlim 4 R ofst #offset"/></LiveEffect>'
.replace('#offset', offset()),
TIMES = 100; // if shapes are too large, should increase the value.
if (checkBounds) {
// check its size only if it needs, because it's too slow
while (TIMES-- && getMinVisibleSize(p.visibleBounds) > 3) p.applyEffect(xmlstring);
} else {
while (TIMES--) p.applyEffect(xmlstring);
}
return p;
}
function getCenterBounds(op) {
var originalMinSize = getMinVisibleSize(op.visibleBounds);
var p = applyOffset(op, false);
if (getMinVisibleSize(p.visibleBounds) > originalMinSize) {
// in some cases, path p becomes larger for some unknown reason
p.remove();
p = applyOffset(op, true);
}
var b = p.visibleBounds;
if (getMinVisibleSize(b) > 10) {
activeDocument.selection = [p];
executeMenuCommand("expandStyle");
p = activeDocument.selection[0];
if (p.typename == "CompoundPathItem") {
b = findBestBounds(op, p);
}
}
p.remove();
return b;
}
function add_nums(n, xy, s) {
var txt = NUM_LAY.textFrames.add();
txt.contents = n;
txt.textRange.justification = Justification.CENTER;
txt.textRange.characterAttributes.size = s;
txt.position = [xy[0] - txt.width / 2, xy[1] + txt.height / 2];
}
function strokeComPath(compoundPath) {
var p = compoundPath.pathItems,
l = p.length,
i = 0;
for (; i < l; i++) {
// !p[i].closed && p[i].closed = true;
// p[i].stroked = true;
// p[i].filled = false;
}
};
Result (added the layer with numbers after run the script):
Final outlined version with numbers and the 'color palette'
Note: you have to ungroup and unmask the color artwork before you run the Script #2.
Here is the results for the rest examples:
As you can see the 'final' artwork still need a quite amount of additional manual work: to move or remove extra numbers.
And it makes sense to reduce the number of colors in original color artworks (perhaps it's possible to do with a script to some extent, as well).
Related
2D Battleship game demo. Suggestions and logic corrections welcome
I am a novice programmer. I used a Random number generator to place ships. Placing ships needs few tweaks. Will use loops to make use of all the available space and also incorporate vertical placement. Please review my code below and suggests any tweaks or logic corrections. Thank you. var rn = 0; var bx = by = cx = cy = 0; var guess; var guesses var fsunk; var bhit; var c1hit; var userchoices = []; var battleship = []; //ship position stored in array var cruiser1 = []; var sunk = []; //ship status array 0 = floating, 1 = sunk //var cruiser2 = []; //var destroyer1 = []; //var destroyer2 = []; //var destroyer3 = []; //var submarine1 = []; //var submarine2 = []; //var submarine3 = []; //var submarine4 = []; function rng() //Generates Random Numbers { rn = Math.floor(Math.random() * 5); console.log("rn = " + rn); return rn; } function spawn() //uses rng() to get x and y coordinates { bx = rng(); by = rng(); cx = rng(); cy = rng(); position(); } function position() // ship positon generated { battleship[0] = ("" + bx + by); battleship[1] = ("" + bx + (by + 1)); battleship[2] = ("" + bx + (by + 2)); battleship[3] = ("" + bx + (by + 3)); cruiser1[0] = ("" + cx + cy); cruiser1[1] = ("" + cx + (cy + 1)); cruiser1[2] = ("" + cx + (cy +2)); } function play() // onclick function { console.log("game start"); sunk[0] = 0; //battleship status flag sunk[1] = 0; //cruiser1 status flag bhit = 0; //battleship hit counter set to zero c1hit = 0; //cruiser hit counter set to zero console.log("sunk array = " + sunk[0] + " " + sunk[1]); fsunk = 0; //fleet status spawn(); position(); console.log("battleship coordinates" + battleship); console.log("cruiser coordinates" + cruiser1); game(); } function game() { while(fsunk == 0) { if(sunk[0] && sunk[1] == 1) //check if fleet is sunk { alert("enemy fleet sunk"); fsunk = 1; } else { userinput(); //calls function to get users input if(battleship.includes(guess)) //checks if users guess is in battleship array { console.log("game.if") //just to see which clause is executing bhit++; //no of hits on battleship incremented if(bhit == 4) { sunk[0] = 1; //battleship status set to sunk console.log("battleship sunk"); } } else if (cruiser1.includes(guess)) { console.log("game.elseif") c1hit++; //no of hits in cruiser1 incemented if(c1hit == 3) { sunk[1] = 1; //cruiser1 status set to sunk console.log("cruiser1 sunk"); } } } } } function userinput() { guess = prompt("Enter the grid coordinates. Do not use space. X-coordinates 0-6, Y-coordinates 0-6."); console.log("users input = " + guess); while(guess < 0 || guess > 66 || userchoices.includes(guess)) //checks user input for repeated strikes or out of range. Suggest better way if possible. this is just bad code { alert("You have entered an invalid coordinate."); guess = prompt("Try Again!"); } guesses++; //increments no of guessess userchoices.push(guess); //add users guess to array return guess; } I have added comments to better explain each line of code. But feel free to ask for any explanations and do correct my logic wherever required. Thank you Edit: I am currently working on incorporating more ships and vertical positioning. I am aware that requires major changes to spawn() and position(). I am also extending the board to the standard 10 X 10 grid and that also makes validating users input way easier.
Histogram mean value does not change in loop
I wrote a script to compare my top layer set to difference blend mode against 3 other layers. The bottom layer is an exact copy of the top layer. I look at the documents histogram, check to see if the mean < 1 (later I'm going to account for a little variance, hence 1), color the layer in the layer palette red and turn the layer off....repeat. The problem I am having is that my mean values for each iteration remain the same. I have written the files to a csv so I could debug the issue and I can't figure out why this is occurring. #target photoshop // call the source document var srcDoc = app.activeDocument; var check = srcDoc.artLayers.getByName("check"); var layers = srcDoc.layers; var docHisto = srcDoc.histogram; //var Name = app.activeDocument.name.replace(/\.[^\.]+$/,''); //var Ext = decodeURI(app.activeDocument.name).replace(/^.*\./,''); //var Path=app.activeDocument.path; startRulerUnits = app.preferences.rulerUnits; startTypeUnits = app.preferences.typeUnits; var original_units = app.preferences.rulerUnits; //app.preferences.rulerUnits = Units.PERCENT; //width and height of image var imageWidth = srcDoc.width.as('px'); var imageWidth = srcDoc.width.value; var imageHeight = srcDoc.height.as('px'); var imageHeight = srcDoc.height.value; var numPixels = imageWidth*imageHeight; var layNum = srcDoc.artLayers.length; srcDoc.activeLayer = check; check.blendMode = BlendMode.DIFFERENCE; for (var i = 1; i < layNum; ++i) { var meanval = EvalHisto(docHisto, i) ; alert(meanval) if (meanval < 1){ alert("same"); srcDoc.activeLayer = layers[i]; layercolor("red"); } layers[i].visible = false; } function layercolor(colour) { switch (colour.toLocaleLowerCase()){ case 'red': colour = 'Rd '; break; case 'orange' : colour = 'Orng'; break; case 'yellow' : colour = 'Ylw '; break; case 'yellow' : colour = 'Ylw '; break; case 'green' : colour = 'Grn '; break; case 'blue' : colour = 'Bl '; break; case 'violet' : colour = 'Vlt '; break; case 'gray' : colour = 'Gry '; break; case 'none' : colour = 'None'; break; default : colour = 'None'; break; } var desc = new ActionDescriptor(); var ref = new ActionReference(); ref.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') ); desc.putReference( charIDToTypeID('null'), ref ); var desc2 = new ActionDescriptor(); desc2.putEnumerated( charIDToTypeID('Clr '), charIDToTypeID('Clr '), charIDToTypeID(colour) ); desc.putObject( charIDToTypeID('T '), charIDToTypeID('Lyr '), desc2 ); executeAction( charIDToTypeID('setd'), desc, DialogModes.NO ); }; function EvalHisto(histogram, i) { var mean = 0; var total = 0; var luminosity = srcDoc.histogram; var red = srcDoc.channels["Red"].histogram; var green = srcDoc.channels["Green"].histogram; var blue = srcDoc.channels["Blue"].histogram; var datFile = new File("~/Desktop/Histogram" + i + ".csv"); datFile.open("e"); datFile.writeln("Level,Luminosity,Red,Green,Blue, Mean\r"); for (var n = 0; n < histogram.length; n++) { total = total + histogram[n]; //calculate total histogram value over the 255 color space } for (var m = 0; m < histogram.length; m++){ mean = mean + (m*histogram[m]/total); //calculate running mean of intensity datFile.writeln(m + "," + luminosity[m] + "," + red[m] + "," + green[m] + "," + blue[m] + "," + mean + "\r"); } datFile.close(); return mean; };
I found the mistake, I needed to move var docHisto = srcDoc.histogram; into my layer loop, otherwise I would only ever be finding the histogram resulting from the first layer and second layer blend.
How to generate random divs but not overlap
I want to generate random divs but not overlap with JavaScript. Just like this: random div example But when the number of DIVS gets larger the browser will be blocked! Then I use the Web Workers to generate the random divs,the browser will be blocked too. here is my main js code: self.onmessage = function(e){ var itemslength = e.data; console.log(itemslength); var position = []; for(var i = 0 ; i < itemslength ; i++){ var length = position.length; if(length == 0){ x = 900*Math.random(); y = 400*Math.random(); var relxy = [x,y]; position.push(relxy); }else{ var flag = true; x = 900*Math.random(); y = 400*Math.random(); do{ console.log('RUNNING!!'); x = x + 50; y = y + 50; var relxy = [x,y]; for(var j = 0 ; j < length ; j++){ var x1 = position[j][0]; var y1 = position[j][1]; var z = (x - x1)*(x - x1) + (y - y1)*(y - y1); if(z > 10000){ flag = flag && true; }else{ flag = flag && false; } } }while(!flag); position.push(relxy); } } console.log('FINSHED!!'); postMessage(position); }; <script> var worker = new Worker('./js/bubbleworker.js'); var itemslengh = $('.items').length; worker.postMessage(itemslengh); worker.onmessage = function(data){ for(var i=0;i<data.data.length;i++){ var x = data.data[i][0]; var y = data.data[i][1]; $('.items:eq(' + i + ')').css({ 'position':'absolute','left':x + 'px','top':y + 'px' }) } } </script> Am i right? Someone who has a better idea? Thanks!
Reset values Issue
Building a dice game and you get 3 rolls. Once you finish your turn i'm trying to have a "reset" button that will reset the values back to the original spot so the "next person" can play. The values reset as I expected but when I "roll" none of the functions are taking place and i'm pretty new in js so i'm not sure what the problem is. var playerScore = document.getElementById('firstPlayerScore'); var rollButton = document.getElementById('roll_button'); var dice1 = new dice(1); var dice2 = new dice(2); var dice3 = new dice(3); var dice4 = new dice(4); var dice5 = new dice(5); var diceArray = [dice1, dice2, dice3, dice4, dice5]; var cargo = 0; var numOfRolls = 0; var cargoButton = document.getElementById('cargo'); var canHaveCargo = false; function restart(){ dice1 = new dice(1); dice2 = new dice(2); dice3 = new dice(3); dice4 = new dice(4); dice5 = new dice(5); diceArray = [dice1, dice2, dice3, dice4, dice5]; cargo = 0; numOfRolls = 0; canHaveCargo = false; addGlow(); updateDiceImageUrl(); document.getElementById("dice1").classList.remove('glowing'); document.getElementById("dice2").classList.remove('glowing'); document.getElementById("dice3").classList.remove('glowing'); document.getElementById("dice4").classList.remove('glowing'); document.getElementById("dice5").classList.remove('glowing'); } //dice object function dice(id){ this.id = id; this.currentRoll = 1; this.previousRoll = 1; this.isSelected = false; this.diceImageUrl = "img/dice/dice1.png"; this.roll = function(){ this.previousRoll = this.currentRoll; this.currentRoll = getRandomRoll(1, 6); } } //returns an array of all dice that are not currently selected so they can be rolled. function getRollableDiceList(){ var tempDiceList = []; for(var i = 0; i < diceArray.length; i++){ if(!diceArray[i].isSelected){ tempDiceList.push(diceArray[i]); } } return tempDiceList; } // gets a random number between min and max (including min and max) function getRandomRoll(min,max){ return Math.floor(Math.random() * (max-min + 1) + min); } // calls the roll function on each dice function rollDice(rollableDiceList){ for(var i = 0; i < rollableDiceList.length; i++){ rollableDiceList[i].roll(); } } // updates each dice with the new url for the image that corresponds to what their current roll is function updateDiceImageUrl(){ for(var i = 0; i < diceArray.length; i++){ var currentDice = diceArray[i]; currentDice.diceImageUrl = "http://boomersplayground.com/img/dice/dice" + currentDice.currentRoll + ".png"; //update div image with img that cooresponds to their current roll updateDiceDivImage(currentDice); } } //Displays the image that matches the roll on each dice function updateDiceDivImage(currentDice) { document.getElementById("dice"+currentDice.id).style.backgroundImage = "url('" + currentDice.diceImageUrl +"')"; } // returns an array of all function getNonSelectedDice(){ var tempArray = []; for(var i = 0; i < diceArray.length; i++){ if(!diceArray[i].isSelected){ tempArray.push(diceArray[i]); } tempArray.sort(function(a, b){ return b.currentRoll - a.currentRoll; }); } return tempArray; } function getSelectedDice(){ var selectedDice = []; for(var i = 0; i < diceArray.length; i++){ if(diceArray[i].isSelected){ selectedDice.push(diceArray[i]); } } return selectedDice; } //boolean variables var shipExist = false; var captExist = false; var crewExist = false; //checks each dice for ship captain and crew. Auto select the first 6, 5 , 4. function checkForShipCaptCrew(){ //array of dice that are not marked selected var nonSelectedDice = getNonSelectedDice(); for(var i = 0; i < nonSelectedDice.length; i++){ //temp variable that represents the current dice in the list currentDice = nonSelectedDice[i]; if (!shipExist) { if (currentDice.currentRoll == 6) { shipExist = true; currentDice.isSelected = true; } } if (shipExist && !captExist) { if (currentDice.currentRoll == 5) { captExist = true; currentDice.isSelected = true; } } if (shipExist && captExist && !crewExist) { if (currentDice.currentRoll == 4) { crewExist = true; currentDice.isSelected = true; canHaveCargo = true; } } } } function addGlow(){ var selectedDice = getSelectedDice(); for (var i = 0; i < selectedDice.length; i++){ var addGlowDice = selectedDice[i]; var element = document.getElementById('dice' + addGlowDice.id); element.className = element.className + " glowing"; } } function getCargo(){ var cargo = 0; var moreDice = getNonSelectedDice(); if (canHaveCargo){ for(var i=0; i < moreDice.length; i++){ cargo += moreDice[i].currentRoll; playerScore.innerHTML = 'You have got ' + cargo + ' in ' + numOfRolls + ' rolls!'; } } else { alert("You don't have Ship Captain and the Crew yet!"); } } rollButton.addEventListener('click', function(){ //generate rollable dice list if (numOfRolls < 3) { var rollableDiceList = getRollableDiceList(); //roll each dice rollDice(rollableDiceList); //update dice images updateDiceImageUrl(); getNonSelectedDice(); // //auto select first 6, 5, 4 (in that order) checkForShipCaptCrew(); addGlow(); // //adds a red glow to each dice that is selected numOfRolls++; } }); cargoButton.addEventListener('click', getCargo); var startButton = document.getElementById('restart'); startButton.addEventListener('click', restart); http://boomer1204.github.io/shipCaptainCrew/ Here is a link to the live game since it's the only way I can describe the problem since I don't know what's not working. If you roll the dice a couple times the dice will get a blue border and be "saved" according to the rules. Now after you hit th restart button that doesn't happen anymore. Thanks for the help in advance guys
Just add this to your restart() function restart(){ ... shipExist = false; capExist = false; crewExist = false; ... }
It's hard to replicate without a fiddle, but it seems that you are adding and removing the 'glowing' class using separate processes. Have you tried adding the glowing class the same way you are removing it? element.classList.add("glowing") See an example within a fiddle: https://jsfiddle.net/5tstf2f8/
Detect coherent neighbors / neighborhood in 2d array
Im having an arbitrary 2d array and each field has an id and a teamid (here illustrated as colors 1). I want for every neighborhood an array with the ids in it. A neighborhood consists of fields with neighbors with the same teamid horizontally and vertically (not diagonally) e.g.: This is what i have: array[0][0] = {id:1,teamId:1} array[1][0] = {id:2,teamId:1} array[2][0] = {id:3,teamId:0} array[3][0] = {id:4,teamId:2} array[4][0] = {id:5,teamId:2} array[5][0] = {id:6,teamId:0} array[0][1] = {id:7,teamId:1} array[1][1] = {id:8,teamId:1} array[2][1] = {id:9,teamId:1} array[3][1] = {id:10,teamId:2} array[4][1] = {id:11,teamId:2} array[5][1] = {id:12,teamId:0} //and so on.. This is what i want: neighborhood[1] = [1,2,7,8,9,13,14] neighborhood[2] = [4,5,10,11] neighborhood[3] = [16,22,23,24,29,30] neighborhood[4] = [25,31,32,37,38] neighborhood[5] = [35,41] I am not searching for the images, but for the array neighborhood thanks in advance!
You can use the logic from dots and block games. A block belongs to a player if he has surrounded it with the walls. So, you need for each cell also 4 walls except for the outer cells. To test if a cell is closed you can use 4 class variables: var Block = function() { this.isclosed=0; this.left=0; this.top=0; this.right=0; this.bottom=0; return this; } Block.prototype = { isClosed : function () { if (this.isclosed==true) { return false; } else if (this.left && this.top && this.right && this.bottom) { this.isclosed=true; return true; } else { return this.left && this.top && this.right && this.bottom; } } } You can try my implementations of dots and blocks game # https://dotsgame.codeplex.com/.
The method for solving this issue is refered as Connected Component Labelling A similar question was asked once before from which i have my solution: // matrix dimensions var row_count = 20; var col_count = 20; var numOfTeams = 2; // the input matrix var m = []; // the labels, 0 means unlabeled var label = []; var source = document.getElementById("source"); for (var i = 0; i < row_count; i++) { var row = source.insertRow(0); m[i] = []; label[i] = []; for (var j = 0; j < col_count; j++) { //m[i][j] = Math.round(Math.random()); m[i][j] = getRandomInt(0, numOfTeams + 1); label[i][j] = 0; var cell1 = row.insertCell(0); cell1.innerHTML = m[i][j]; } } // direction vectors var dx = [1, 0, -1, 0]; var dy = [0, 1, 0, -1]; function dfs(x, y, current_label, team) { if (x < 0 || x == row_count) return; // out of bounds if (y < 0 || y == col_count) return; // out of bounds if (label[x][y] || team != m[x][y]) return; // already labeled or not marked with 1 in m // mark the current cell label[x][y] = current_label; // recursively mark the neighbors for (var direction = 0; direction < 4; ++direction) { dfs(x + dx[direction], y + dy[direction], current_label, team); } } function find_components() { var component = 0; for (var i = 0; i < row_count; ++i) { for (var j = 0; j < col_count; ++j) { if (!label[i][j] && m[i][j]) dfs(i, j, ++component, m[i][j]); } } } find_components(); var result = document.getElementById("result"); for (var i in label) { var string = "" var row = result.insertRow(0); for (var j in label[i]) { string += label[i][j] + " " var cell1 = row.insertCell(0); cell1.innerHTML = label[i][j]; } } function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min)) + min; } table tr td { min-width: 14px } <div style="float:left"> <table id="source"></table> </div> <div style="float:right"> <table id="result"></table> </div>