Contiguous Block Plotting/Removal Logic - actionscript-3

I've been attempting to write some logic for a program I have been working on and have run into some difficulty.
Essentially what I'm creating on my stage programmatically is a 4x4 grid (16 blocks), which looks just like this:
The user's task is to plot a contiguous shape onto the grid by clicking on the blocks and their shape should feature no gaps and no diagonally plotted blocks, for example the following would be a legal shape:
However, the following shape wouldn't be and would throw out an error to the user in the form of a pop-up graphic:
The plotting process for the grid is associated with a 4x4 virtual representation of the grid in Boolean Array form and looks like this:
public static var ppnRowArray1:Array = [false,false,false,false];
public static var ppnRowArray2:Array = [false,false,false,false];
public static var ppnRowArray3:Array = [false,false,false,false];
public static var ppnRowArray4:Array = [false,false,false,false];
public static var ppnColumnArray:Array = [ppnRowArray1,ppnRowArray2,ppnRowArray3,ppnRowArray4];
As the user clicks and selects a block, changing the colour property to brown, the relevant boolean property in my 'virtual grid representation' array will change from false to true. If a plot is illegally made then this property is changed back to false and the user is then invited to try their next plot again.
I have managed to write the code which forces the user to plot a legal shape and works out when an illegal plot has been made, but I now need to write the logic for when a user de-selects a block from an existing legal shape, making it non-contiguous and this is where my problem lies.
Here is the working solution as it stands.
//---------------------------------------------------------------------
public static function ppnCountSetCells():int
{
//Count Each 4x4 Grid Cell
var count:int = 0;
for (var row=0; row<=3; row++)
{
for (var col=0; col<=3; col++)
{
if (ppnColumnArray[col][row])
{
count++;
}
}
}
return count;
}
//---------------------------------------------------------------------
public static function ppnBlockValid():Boolean
{
if (ppnCountSetCells() > 1)
{
for (var row=0; row<=3; row++)
{
for (var col=0; col<=3; col++)
{
if (ppnColumnArray[col][row] == true)
{
// Check if we are connected to another set square
var validNeighbours:int = 0;
// Check North
if (row > 0)
{
if (ppnColumnArray[col][row - 1] == true)
{
validNeighbours++;
}
}
// Check South
if (row < 3)
{
if (ppnColumnArray[col][row + 1] == true)
{
validNeighbours++;
}
}
// Check West
if (col > 0)
{
if (ppnColumnArray[col - 1][row] == true)
{
validNeighbours++;
}
}
//-----------------------------------------------------------------------------------------
// Check East
if (col < 3)
{
if (ppnColumnArray[col + 1][row] == true)
{
validNeighbours++;
}
}
//-----------------------------------------------------------------------
if (validNeighbours < 1)
{
return false;
}
//-----------------------------------------------------------------------
}
}
}
}
return true;
}
//---------------------------------------------------------------------
function addBlock(e:MouseEvent):void
{
//trace("You Have Clicked On Grid Block Number: " + e.currentTarget.id);
if (InterfaceButtons.panelOpen == false)
{
//Listen to see if the block click is adjoining and pass back to see if it is valid on the grid
var col:int = (e.currentTarget.id - 1) % 4;
var row:int = (e.currentTarget.id - 1) / 4;
ppnColumnArray[col][row] = true;
addOrRemove = "add";
ppnBlockValid();
//Get the Block Valid Result (True or False) and pass it into a Boolean variable to use later
ppnGridError = ppnBlockValid();
trace("Is This Valid? " + ppnBlockValid());
//----------------------------------------------------------------------------------------------
//Push Blocks Selected into Array
ppnShapeArray[e.currentTarget.id] = true;
trace(ppnShapeArray);
//----------------------------------------------------------------------------------------------
//Add 1 to the block count which directly effects the final outcome depending on ++ or --
ppnBlocksSelected++;
PlantPopNitDesignPlot.ppnPlotMade = false;
//Hide Block to Reveal Brown One
e.currentTarget.alpha = 0;
//-----------------------------------------------------------------------------------------------
//Output an error if one is present on Click based on gridError Boolean Variable
ppnOutputAddError();
if (ppnGridError == false)
{
//Restore the block's alpha property as it isn't allowed to be selected, removing counter by one -- and changing final output accordingly
e.currentTarget.alpha = 1;
ppnBlocksSelected--;
ppnColumnArray[col][row] = false;
ppnShapeArray[e.currentTarget.id] = false;
ppnPopulateTotalSiteUnitsTxt();
}
//Update final total
ppnPopulateTotalSiteUnitsTxt();
//Call again to do dynamic colour font change should total exceed 10
ppnPopulateTotalSiteUnitsTxt();
//Added in to make sure it executes every time if an error is made.
if (ppnGridError == true)
{
e.currentTarget.removeEventListener(MouseEvent.CLICK, addBlock);
e.currentTarget.addEventListener(MouseEvent.CLICK, removeBlock);
}
}
}
function removeBlock(e:MouseEvent):void
{
if (InterfaceButtons.panelOpen == false)
{
var col:int = (e.currentTarget.id - 1) % 4;
var row:int = (e.currentTarget.id - 1) / 4;
ppnColumnArray[col][row] = false;
addOrRemove = "remove";
ppnBlockValid();
ppnGridError = ppnBlockValid();
trace("Is This Removal Valid? " + ppnBlockValid());
//trace("You Have Clicked On Grid Block Number: " + e.currentTarget.id);
e.currentTarget.alpha = 1;
ppnShapeArray[e.currentTarget.id] = false;
//trace("ppnShapeArray - " + ppnShapeArray);
//---------------------------------------------------------------------
ppnBlocksSelected--;
PlantPopNitDesignPlot.ppnPlotMade = false;
//Output an error if one is present on Click based on gridError Boolean Variable
ppnOutputRemoveError();
if (ppnGridError == false)
{
//Restore the block's alpha property as it isn't allowed to be selected, removing counter by one -- and changing final output accordingly
e.currentTarget.alpha = 0;
ppnBlocksSelected--;
ppnColumnArray[col][row] = true;
ppnShapeArray[e.currentTarget.id] = true;
ppnPopulateTotalSiteUnitsTxt();
}
//Update Final Total
ppnPopulateTotalSiteUnitsTxt();
//Call again to do dynamic colour font change should total falls below 10
ppnPopulateTotalSiteUnitsTxt();
//Added in to make sure it executes every time.
if (ppnGridError == true)
{
e.currentTarget.addEventListener(MouseEvent.CLICK, addBlock);
e.currentTarget.removeEventListener(MouseEvent.CLICK, removeBlock);
}
}
}
}
}
//---------------------------------------------------------------------
Now, for most occurences this logic works and detects illegal plots when adding or removing a block to the shape, however recently I discovered that when I have 5 > blocks in the shape, the logic for detecting an error on removal fails in certain circumstances.
A few examples of shapes being declared true and legal when they are not (when a block has been removed) are as follows:
I can see that it is the logic written in my 'ppnBlockValid():Boolean' function that needs adjusting to compensate for these outputs. It seems you can only remove a block providing that the neighbouring blocks are still joined to something else. While this works for smaller shapes, larger shapes (e.g. 5 blocks or more) can theoretically be split down the middle, so I think the code needs adjusting to account for this.
But how? Any help on this would be greatly appreciated.
Many thanks in advance and if you need any further information from me please let me know.
Cheers,
Joel
Edited
Thank you very much for providing that enhanced code and explanation #dhc I really appreciate it, but I'm still a little confused about how to implement all this properly.
Here is my current 'ppnBlockValid' function code based on your suggestion below:
public static function ppnBlockValid():Boolean
{
ppnIslands = [];
ppnAddNewIsland = [];
ppnAddToExistingIsland = [];
if (ppnCountSetCells() > 1)
{
for (var row=0; row<=3; row++)
{
for (var col=0; col<=3; col++)
{
if (ppnColumnArray[col][row] == true)
{
var addedToIsland = false;
// Check if we are connected to another set square
var validNeighbours:int = 0;
// Check North
if (row > 0)
{
if (ppnColumnArray[col][row - 1] == true)
{
validNeighbours++;
}
//----------------------------------
//ISLAND CHECK
if (ppnColumnArray[col][row - 1])
{
ppnAddToExistingIsland.push([col,row - 1],[col,row]);
addedToIsland = true;
}
}
// Check South
if (row < 3)
{
if (ppnColumnArray[col][row + 1] == true)
{
validNeighbours++;
}
}
// Check West
if (col > 0)
{
if (ppnColumnArray[col - 1][row] == true)
{
validNeighbours++;
}
//----------------------------------
//ISLAND CHECK
if (ppnColumnArray[col - 1][row])
{
ppnAddToExistingIsland.push([col - 1,row],[col,row]);
addedToIsland = true;
}
}
//-----------------------------------------------------------------------------------------
// Check East
if (col < 3)
{
if (ppnColumnArray[col + 1][row] == true)
{
validNeighbours++;
}
}
//-----------------------------------------------------------------------
if (! addedToIsland)
{
ppnIslands.push([col,row]);
}
//-----------------------------------------------------------------------
if (ppnIslands.length >= 2 && addOrRemove == "remove")
{
trace("TWO ISLANDS HAVE BEEN FORMED AND AN ERROR SHOULD BE OUTPUT");
validNeighbours--;
}
/**/
//return (ppnIslands.length<=1);// 0 islands is valid also!
//-----------------------------------------------------------------------
if (validNeighbours < 1)
{
return false;
}
//-----------------------------------------------------------------------
}
}
}
}
return true;
}
//---------------------------------------------------------------------
I have been using the following shape as my experiment with the code:
Based on the above code and example shape, my current trace output is:
ppnIsland = |0,0|,0,2
ppnAddNewIsland =
ppnAddToExistingIsland = 0,0, 1,0, 1,0, 2,0, 2,0, 3,0, 1,0, 1,1, 1,1, 1,2, 0,2, 1,2, 1,2, 2,2, 2,2, 3,2
It appears that despite the shape being contiguous, the code I've tried interpreting from you is finding an additional island, in this case 'Col: 0, Row: 2', before a block removal has even taken place? Is this right?
This code of course does output an error if I try to remove the middle (red) block as the 'ppnIsland' array contains > 1 island, however I don't think its detecting the correct output?
Do I need to cross-reference the 'ppnIsland' and 'ppnAddToExistingIsland' arrays using the indexOf command to check if either element is part of an existing island?
Joel

You could track "islands" as separate lists as you process (in ppnBlockValid). So, for instance (caps are selected tiles):
a B c d
e F g H
i J k L
m n o p
When you process row one, you create an island for B. When you process row 2, you find B connected to F, so the island becomes [B,F]. Then, when you encounter H, which is not connected, you have two islands: [B,F],[H]. On row 3, your islands would look like [B,F,J],[H,L]. If K were selected, you'd find J and L connected, and you'd consolidate them into one island. You only have to check the current row for connections between islands, so you'd only have to check J and L in this case.
If you end up with one island, you're fine, otherwise not. This is, unfortunately, an order n**2 search, but your grid is small so it probably doesn't mean much.
Something like this (WARNING: not debugged code):
private var islands : Array;
public function ppnBlockValid():Boolean {
islands = [];
if (ppnCountSetCells() > 1) {
for (var row=0; row<=3; row++) {
for (var col=0; col<=3; col++) {
if (ppnColumnArray[col][row]) {
var addedToIsland = false;
// Check if we are connected to another set square
// Check North
if (row > 0) {
if (ppnColumnArray[col][row-1]) {
addToExistingIsland([col,row-1],[col,row]);
addedToIsland = true;
}
}
// Check South - not needed since next row will catch this on North
// Check West
if (col > 0) {
if (ppnColumnArray[col-1][row]) {
addToExistingIsland([col-1,row],[col,row]);
addedToIsland = true;
}
}
// Check East - not needed since next col will catch this on West
if (!addedToIsland) { addNewIsland([col,row]); }
}
}
}
}
return (islands.length<=1); // 0 islands is valid also!
}
You just have to implement "addNewIsland" and "addToExistingIsland". addNewIsland is easy: just add a new element to the islands array. You may want to use a string ID for the tiles instead of an array (e.g. "01" instead of [0,1]) since it may make checking for existing elements easier (e.g. you can use indexOf to find a tile in an island).
addToExistingIsland is a bit trickier: you have to check if either element is part of an existing island, and consolidate those islands if so. If not, the new tile just gets appended to the island.
You don't need to maintain 3 arrays, just one island array that, at the end of ppnBlockValid, will either be length > 1 (invalid), or less (valid).
You cannot just populate the "ppnAddNewIsland" and "ppnAddToExistingIsland" arrays-- these should be functions. If ppnIslands is a class variable, then your addNewIsland function may look something like this:
private function addNewIsland(who) {
ppnIslands.push([who]);
}
... the only optimization I'd probably make is to turn who (passed as an array [col,row]) into a character string, as I mentioned, to make finding whether a tile exists in an island easier.
Then, you need a function like:
private function addToExistingIsland( existing_island_tile, new_tile ) {
}
This function must:
1. check whether these two tiles already exist in the same island (just return if so)
2. check whether new_tile is already on an island (consolidate the two islands into one if so)
3. otherwise, just add new_tile to the island that existing_island_tile is a member of
The code I presented should work pretty much as-is-- although you could add back in the "validNeighbours" logic if you want a quick exit for isolated tiles.

Related

How to create a matrix in a loop for hittesting with colors without duplicate colors

I am trying to create a matrix with color hexagons that will allow for object hit-testing. This color matrix will sit behind a regular hexagon matrix(one without the matrix cells colored). I will mouse over and check for the color in the hidden matrix to indicate which object my mouse is over so I can simulate object detection. This should be straight forward but for some reason I cannot get the expected results.
I have a parent loop which loops through creating rows of the matrix. Each iteration calls the following function to paint the hexagon(which is defined in a collection of points made available through a hexagon service).
constructor(
private hexagons: HexagonService,
private state: StateService,
private randomColors: RandomColorService,
private drawingContextService: DrawingContextService) {
}
async DrawHexagonAsync(hidecolors: boolean): Promise<any> {
const color = this.randomColors.next();
let strokestyle = this.state.strokeStyle;
const context = this.drawingContextService.context;
console.log(color); // Look for duplicates in console
if (hidecolors) {
strokestyle = `#fff`;
}
context.strokeStyle = strokestyle;
context.lineWidth = this.state.lineWidth;
context.fillStyle = color;
context.lineJoin = 'round';
context.lineCap = 'round';
const points = this.hexagons.getHexagonPoints();
context.beginPath();
let x = this.drawingContextService.location.x + points[0].x;
let y = this.drawingContextService.location.y + points[0].y
context.moveTo(x,y);
// move to the start point
for (let i = 1; i < points.length; i++) {
x = points[i].x + this.drawingContextService.location.x;
y = points[i].y + this.drawingContextService.location.y;
context.lineTo(x, y);
context.stroke();
}
context.fill();
context.closePath();
}
At first glance it looks like it is working and creates a lovely hex matrix like the following:
At closer inspection, I noticed that some of the contiguous items get rendered as duplicate colors. Since, I need unique colors for hit-testing this will not do. I use a color service to iterate through and create unique colors.
// color service.
#Injectable({provideIn: 'root'})
export class RandomColorService {
colors: string[] = [];
index = 0;
constructor() {
while (this.colors.length < 5000) {
let random = () => {
let n = Math.random() * 256;
return Math.floor(n).toString(16);
}
// make sure there are no duplicate colors.
const color = `#${random()}${random()}${random()}`;
if (!this.colors.includes(color)) {
this.colors.push(color);
}
}
}
public next() {
if (this.index >= 5000) {
this.index = 0;
}
return this.colors[this.index++];
}
}
Initially, I put the checking code to make sure that the colors were unique but the same duplicate color problem occurs with or without the code check. The next function just returns the unique colors as needed and moves to the next.
I even put a console.log in the code to check and I don't see any duplicate log messages, yet the colors are clearly duplicates.
Using the following function against the colors clearly indicates that the colors are not similar(but in fact duplicate)
// Gets a pixel from the canvas and extracts the color.
onclick(evt) {
var pixelData = this.drawing.context.getImageData(evt.x, evt.y, 1, 1).data;
var hex = "#" + ("000000" + this.rgbToHex(pixelData[0], pixelData[1], pixelData[2])).slice(-6);
console.log(hex);
}
rgbToHex(r, g, b) {
if (r > 255 || g > 255 || b > 255)
throw "Invalid color component";
return ((r << 16) | (g << 8) | b).toString(16);
}
So naturally the question is, what am I missing?
The bug is in the color service. The numbers need to be padded with '0'. If the color is not 6 digits then the color change fails and the previous color is retained and used.
return Math.floor(n).toString(16).padStart(2, '0');
// color service.
#Injectable({provideIn: 'root'})
export class RandomColorService {
colors: string[] = [];
index = 0;
constructor() {
while (this.colors.length < 5000) {
let random = () => {
let n = Math.random() * 256;
return Math.floor(n).toString(16);
}
// make sure there are no duplicate colors.
const color = `#${random()}${random()}${random()}`;
if (!this.colors.includes(color)) {
this.colors.push(color);
}
}
}
public next() {
if (this.index >= 5000) {
this.index = 0;
}
return this.colors[this.index++];
}
}

AS3 How can I get back the object that being dragged to Item

I have board with 16 objects(MovieClip), every one of them have a unique instance name (slot1-16).
I'm trying to make a drag and drop code that return the instance name of the object dragged on it.
function fl_ReleaseToDrop(evt:MouseEvent):void {
var object = evt.currentTarget;
if(object is textBox || object is UILoader)
{
for(var i:int = 1; i < 16; i++){
//Checks the correct drop target
if (object.hitTestObject(getChildByName("slot" + i)))
{
trace("slot" + i);
slot(getChildByName("slot" + i)).gotoAndStop(3);
}else{
object.x = xPos; //If not, return the clip to its original position
object.y = yPos;
}
}
object.stopDrag();
}
}
What really happened is that the only place that i can drag in to is slot1, other slots are not working.
In your current code, if the 1st hit test with slot1 object fails, you return your dragged object to its original position and of course all the hit tests with other slots will fail.
So you should return your object to its original position just after doing the hit test with all objects. You can use, for example, a Boolean var to know if there is at least one succeeded hit test, in that case you don't need to return your dragged object to its original position :
function fl_ReleaseToDrop(evt:MouseEvent):void
{
var object = evt.currentTarget;
var hit_test:Boolean = false;
if(object is textBox || object is UILoader)
{
for(var i:int = 1; i < 16; i++)
{
if (object.hitTestObject(getChildByName("slot" + i)))
{
hit_test = true;
slot(getChildByName("slot" + i)).gotoAndStop(3);
}
}
if(!hit_test)
{
object.x = xPos;
object.y = yPos;
}
object.stopDrag();
}
}
Hope that can help.

Pairing a draggable object to a target object in AS3

I'm currently stuck with my approach below. I'm not entirely sure if using "hitTestObject" method is appropriate in pairing the pieces to their respective place. I was able to at least match the chess piece to their respective location (that's the best I can do and I feel i'm doing it wrong) but I'm now stuck in counting how many pieces are actually in their correct places. e.g. when I move the pawn to a different tile, it will still count as one, I also want to avoid duplicate counting, example, If pawn is already in the correct location, it will just count as 1, and if it was moved, then that count will be removed. Only count the pieces that are in the correct tile.
My goal here is to be able to make all the chess pieces draggable and determine if they're in their respective location. If ALL the chess pieces are in their location, it will trace or call a function.
Thank you!
import flash.events.Event;
import flash.display.MovieClip;
import flash.events.MouseEvent;
/* Declaring an X and Y variable to be used as a reset container */
var xPos: int, yPos: int;
/* Attaching event listeners for each chess piece */
addListeners(
king, queen, bishop_1, bishop_2, knight_1, knight_2, rook_1, rook_2,
pawn_1, pawn_2, pawn_3, pawn_4, pawn_5, pawn_6, pawn_7, pawn_8);
/* Getting the original x and y postion to be used as a reset */
function getPosition(currentTarget: Object): void {
xPos = currentTarget.x;
yPos = currentTarget.y;
}
/* Function to get the suffix value of an object. example, I need to get the value 4 from "pawn_4" */
function getLastCharInString($s: String, $pos: Number): String {
return $s.substr($s.length - $pos, $s.length);
}
/* A simple function that rotates the chess piece */
function lift(object: Object, rot: Number) {
object.rotation = rot;
}
function dragObject(e: MouseEvent): void {
getPosition(e.currentTarget);
lift(e.currentTarget, -10);
getChildByName(e.currentTarget.name + "_hs").alpha = 1;
e.currentTarget.startDrag();
}
/* This variable is supposed to hold the value of each piece that is correctly placed in each tile.
The total score should be 16 as there are 16 pieces. Only correcly placed piece should be added in the total score. */
var counter:int;
function stopDragObject(e: MouseEvent): void {
var curretTarget = e.currentTarget.name;
lift(e.currentTarget, 0);
/* Hide active hotspots */
getChildByName(e.currentTarget.name + "_hs").alpha = 0;
var multiplePieceSufix = Number(getLastCharInString(curretTarget, 1));
if (multiplePieceSufix >= 1) {
/* Boolean variables that checks whether the current piece is active*/
var isPawn: Boolean = false,
isBishop: Boolean = false,
isKnight: Boolean = false,
isRook: Boolean = false,
currentTargeName;
var widthDiff = getChildByName(e.currentTarget.name + "_hs").width - getChildByName(e.currentTarget.name).width / 2;
var heightDiff = getChildByName(e.currentTarget.name + "_hs").height - getChildByName(e.currentTarget.name).height / 2;
if (curretTarget.substr(0, 4) == "pawn") {
isPawn = true;
} else if (curretTarget.substr(0, 6) == "bishop") {
isBishop = true;
} else if (curretTarget.substr(0, 6) == "knight") {
isKnight = true;
} else if (curretTarget.substr(0, 4) == "rook") {
isRook = true;
}
if (isPawn == true) {
/* there are total of 8 pieces of pawn */
for (var w = 1; w < 9; w++) {
currentTargeName = this["pawn_" + w + "_hs"];
if (e.target.hitTestObject(currentTargeName)) {
/* For some reason the chess pieces are not aligning with their "_hs" version, I already checked their registry point and it seem to be normal.
so to fix, I had to manually add some hard coded values to adjust their location. */
e.currentTarget.x = currentTargeName.x - 8;
e.currentTarget.y = currentTargeName.y + currentTargeName.height;
}
}
} else if (isBishop == true) {
for (var x = 1; x < 3; x++) {
currentTargeName = this["bishop_" + x + "_hs"];
if (e.target.hitTestObject(currentTargeName)) {
e.currentTarget.x = currentTargeName.x - 9;
e.currentTarget.y = currentTargeName.y + currentTargeName.height - 18;
}
}
} else if (isKnight == true) {
for (var y = 1; y < 3; y++) {
currentTargeName = this["knight_" + y + "_hs"];
if (e.target.hitTestObject(currentTargeName)) {
e.currentTarget.x = currentTargeName.x - 8;
e.currentTarget.y = currentTargeName.y + currentTargeName.height;
}
}
} else if (isRook == true) {
for (var z = 1; z < 3; z++) {
currentTargeName = this["rook_" + z + "_hs"];
if (e.target.hitTestObject(currentTargeName)) {
e.currentTarget.x = currentTargeName.x - 8;
e.currentTarget.y = currentTargeName.y + 62;
}
}
}
} else {
if (e.target.hitTestObject(getChildByName(e.currentTarget.name + "_hs"))) {
/* Again, I'm not sure why the pieces are not aligning as intended.
modX and modY is a holder for the adjustment value. I'm not comfortable
seeing this approach myself, but I also run out of ideas how to fix it. */
var modX: Number, modY: Number;
if (e.currentTarget.name == "king") {
modX = 11;
modY = 53;
} else {
modX = 11;
modY = 29;
}
e.currentTarget.x = getChildByName(e.currentTarget.name + "_hs").x - modX;
e.currentTarget.y = getChildByName(e.currentTarget.name + "_hs").y + getChildByName(e.currentTarget.name + "_hs").height - modY;
}
}
/* This is supposed to add to the total score or count of how many pieces are placed correctly.
Thie problem with thi scounter, as it also counts any piece that is places to any "_hs" */
counter++;
trace(counter);
e.currentTarget.stopDrag();
}
function addListeners(...objects): void {
for (var i: int = 0; i < objects.length; i++) {
objects[i].addEventListener(MouseEvent.MOUSE_DOWN, dragObject);
objects[i].addEventListener(MouseEvent.MOUSE_UP, stopDragObject);
// hide hotspots
getChildByName( objects[i].name + "_hs" ).alpha = 0;
}
}
Source: Download the FLA here
--
Updates:
I have added comments in my code to clarify what I'm trying to accomplish.
I'm planning to do board game in flash which has similar function and behaviour to this. User can drag the object to a specified tile and check wether that object belongs there or not.
After reviewing your code, your question is quite broad. I'm going pair it down to what seems to be your main concern - the score / counting correctly moved pieces.
Right now, you do the following every time an object is dragged:
counter++;
This means that the counter will increment no matter where you drag the object, and no matter how times you drag the object. (so even if the piece was already in the correct spot, if you dragged it a second time it will still increment your counter).
What you need to do, is associate a flag with each object to indicate whether it is in the correct location or not, and set that flag to the appropriate value every time that object is done dragging.
Something like this:
//don't use target, use currentTarget
if (e.currentTarget.hitTestObject(currentTargeName)) {
e.currentTarget.correct = true; //since MovieClips are dynamic, you can just make up a property on them and assign a value to it.
//to fix your alignment:
e.currentTarget.x = currentTargeName.x + ((currentTargetName.width - e.currentTarget.width) * 0.5);
e.currentTarget.y = currentTargeName.y + currentTargeName.height;
}else{
//if the hit test is false, mark it as NOT correct
e.currentTarget.correct = false;
}
Then, later to know the current count, iterate over all the pieces and check their correct value. This would be much easier if all your pieces were in an array.
var allPieces:Array = [king, queen, bishop_1, bishop_2, knight_1, knight_2, rook_1, rook_2,
pawn_1, pawn_2, pawn_3, pawn_4, pawn_5, pawn_6, pawn_7, pawn_8];
function countCorrect():Boolean {
var ctr:int = 0;
for(var i:int=0;i<allPieces.length;i++){
if(allPieces[i].correct) ctr++;
}
return ctr;
}
trace(countCorrect() + " of " allPieces.length " are correct");
As an aside, this best way to do this would be with some custom class files. That would however require a complete refactoring of your code.
Also, you probably don't want to use hitTestObject, as even if a piece is mostly over a neighbor, it will still be true as long as 1 pixel of it's bound touch 1 pixel of the tile. Better would be to do a hitTestPoint on the tile, and pass in the center point of the piece (the the middle of the piece has to be touching the tile for it to count).
//a point that is the center of the events current target (the piece)
var point:Point = new Point();
point.x = e.currentTarget.x + (e.currentTarget.width * 0.5);
point.y = e.currentTarget.y - (e.currentTarget.height * 0.5);
if (currentTargetName.hitTestPoint(point)) {

getThreads() with two Labels gmail snooze script

Hello Im trying to setup EVERY-n-week events for gmail-snooze thus it would be reocurring events not just one time.
I would like to save everyXweek labeled-threads into "newpage" from the already created Xweek labeled-threads in "page" array. I used page = oldLabel.getThreads(0, 100); to grab the Xweek data.
I tried newpage = everyXweekLabel.page; and newpage = page[everyXweekLabel]; but newpage never gets turned into an array with threads.... so it doesnt work...
How i would like this to work:
threads will have multiple labels.
XweekSnooze is our iteration count down timing label
everyXweekSnooze is our static label to reset the thread once placed in inbox
An example would be thread#1 has 1weekSnooze left (which will be removed on the very next iteration) and it also has an every8weekSnooze label (which will need to apply an 8weekSnooze label once the email is put into inbox)
After the 8weekSnooze label is applied it then iterates (7weekSnooze, 6...) down until it is put into the inbox again where the every8weekSnooze label places another 8weekSnooze label back on to iterate through.
I can make this work for n-weeks but I cannot get the EVERY-n-week part to work since I cannot figure out how to pull everyXweek labels from previously pulled page-thread that contains Xweek labels and other labels as well.
Any help you can extend would be greatly appreciated for sure !
Thanks.
JP
function getLabelNameXWeekSnooze(i) {
return "Snooze/" + i + "weekSnooze";
}
function getLabelNameEveryXWeekSnooze(i) {
return "Snooze/" + "every" + i + "weekSnooze";
}
function moveWeekSnoozes() {
var oldLabel, newLabel, page;
for (var i = 1; i <= WEEKS_TRACKED; ++i) {
newLabel = oldLabel;
oldLabel = GmailApp.getUserLabelByName(getLabelNameXWeekSnooze(i));
page = null;
newpage = null;
// Get threads in "pages" of 100 at a time
while(!page || page.length == 100) {
page = oldLabel.getThreads(0, 100);
if (page.length > 0) {
if (newLabel) {
// Move the threads into "today’s" label
newLabel.addToThreads(page);
} else {
// Time to Unsnooze
GmailApp.moveThreadsToInbox(page);
if (MARK_UNREAD) {
GmailApp.markThreadsUnread(page);
}
// after returning mail to inbox please create an
// XweekSnooze label for every thread in page that has associated everyXweekSnooze label
// for example every3weekSnooze thread will need 3weekSnooze label to iterate/delay from
for (var j = 1; j <= WEEKS_TRACKED; ++j) {
// format the label data
everyXweekLabel = GmailApp.getUserLabelByName(getLabelNameEveryXWeekSnooze(j));
XweekLabel = GmailApp.getUserLabelByName(getLabelNameXWeekSnooze(j));
//trying to grab all thread from page that have everyXweek label and save to new page
newpage = everyXweekLabel.page;
if (newpage.length > 0) {
XweekLabel.addToThreads(newpage);
}
}
// Move the threads out of "yesterday’s" label
oldLabel.removeFromThreads(page);
}
}
}
}
For reference, the original Gmail Snooze algorithm can be found here: Gmail Snooze with Apps Script. The algorithm is based on the idea of percolating a set of Gmail labels on threads, using an Apps Script trigger to handle the timing.
Basically, the problem in your question boils down to the need to find a set of GmailThreads that all have two specified labels (that is, the union of the set of threads that have label A and the set that has label B).
Apps Script doesn't provide a direct way of doing this, so you have to do a bit of work. The approach is straightforward: get the set of threads that have label A, iterate through them to check each for label B, and then record the threads that have both.
In this case, we will use an object to record the threads that have been moved to the inbox but now need to be reset. After adjusting all the other labels, we will have a separate reset step. It's important to do this separately, since if you reset a thread while percolating you will accidentally adjust it one too many times.
function moveSnoozes() {
var oldLabel, newLabel, page;
// Set up an empty record of threads that will need
// to be reset
var resets = {};
for (var i = 1; i <= WEEKS_TRACKED; ++i) {
resets[getLabelNameXWeekSnooze(i)] = [];
}
for (var i = 1; i <= WEEKS_TRACKED; ++i) {
newLabel = oldLabel;
oldLabel = GmailApp.getUserLabelByName(getLabelNameXWeekSnooze(i));
page = null;
// Get threads in "pages" of 100 at a time
while(!page || page.length == 100) {
page = oldLabel.getThreads(0, 100);
if (page.length > 0) {
if (newLabel) {
// Move the threads into "today’s" label
newLabel.addToThreads(page);
} else {
// Unless it’s time to unsnooze it
GmailApp.moveThreadsToInbox(page);
if (MARK_UNREAD) {
GmailApp.markThreadsUnread(page);
}
// Now check for everyXweekSnooze label and
// record individual threads that need to be reset
for(var t=0; t<page.length; t++) {
var thread = page[t];
var resetLabel = findResetLabel(thread);
if(resetLabel) {
resets[resetLabel.getName()].push(thread);
}
}
if (ADD_UNSNOOZED_LABEL) {
GmailApp.getUserLabelByName("Unsnoozed")
.addToThreads(page);
}
}
// Move the threads out of "yesterday’s" label
oldLabel.removeFromThreads(page);
}
}
}
// Reset recorded threads
for (var i = 1; i <= WEEKS_TRACKED; ++i) {
var resetLabelName = getLabelNameXWeekSnooze(i);
var resetLabel = GmailApp.getUserLabelByName(resetLabelName);
resetLabel.addToThreads(resets[resetLabelName]);
}
}
// Given a GmailThread, check to see if it has
// any of the everyXweekSnooze labels and return
// the first one found
// #param {Object} thread - GmailThread to examine
// #return {Object} GmailLabel - the first everyXweekSnooze
// label found, or null if none are found
function findResetLabel(thread) {
for (var i = WEEKS_TRACKED; i >= 1; i--) {
var label = getLabelNameEveryXWeekSnooze(i);
var labels = thread.getLabels();
for (var j = 0; j < labels.length; j++) {
if(labels[j].getName() == label) {
return GmailApp.getUserLabelByName(getLabelNameXWeekSnooze(i));
}
}
}
return null;
}

Unknown logical error

I'm assuming it's a logical error because it doesn't return any compiler or run-time errors.
my functions:
function closetest() {
tooclose=false;
for (i=0; i<10; i++) {
if (Math.abs(entry[0]-entry[i])<100) {
tooclose=true;
}
}
}
function xassignment() {
for (i=0; i<10; i++) {
entry[i+1]=entry[i];
}
do {
entry[0] = int(Math.random()*(stage.stageWidth - 30));
closetest();
} while (tooclose == false);
}
Here is where the function is called
mcMain.addEventListener(Event.ENTER_FRAME, moveChar);
function moveChar(event:Event):void {
if (gameOver == false) {
if (enemyTime < enemyLimit) {
enemyTime++;
} else {
var newEnemy = new Enemy();
xassignment();
newEnemy.y=-1*newEnemy.height;
newEnemy.x=entry[0];
addChild(newEnemy);
enemyTime=0;
}
}
}
I'm making a game that involves objects being dropped from a randomly generated x coordinate, I made these functions to make sure that objects aren't being dropped too close to each other but it doesn't appear to have any effect.
You haven't described what effect you are observing, but I noticed this:
for (i=0; i<10; i++) {
if (Math.abs(entry[0]-entry[i])<100) {
tooclose=true;
}
}
This will result in tooclose always being true, because your loop begins at 0, therefore you compare entry[0] with itself. Try starting the loop at 1 instead.
You haven't declared or changed tooclose within xassignment. You should instead return a Boolean from closetest() which will be queried. Another logical error is, you are advancing previous values by 1 in your array, while incrementing the index. So you have i=0, entry[1]=entry[0]. Then i is incremented, entry[2]=entry[1] - bam, entry[2] too starts being requal to entry[0]! And so on, so the entire array of entries now equals to what was rolled on the previous attempt, instead of storing 10 previous values. Fixed that too.
function closetest():Boolean {
for (var i:int=1; i<10; i++) {
if (Math.abs(entry[0]-entry[i])<100)
return true;
}
return false;
}
function xassignment() {
for (i=10; i>0; i--) entry[i]=entry[i-1];
do {
entry[0] = int(Math.random()*(stage.stageWidth - 30));
} while(closetest());
}
Note though, if your closetest implementation will receive a [X,100,300,500,700] array the loop will be endless, as there's no X position that will satisfy your range of 100 for absolute difference. So, fix it with the following:
function xassignment() {
for (i=10; i>0; i--) entry[i]=entry[i-1];
var loops:int=10;
do {
entry[0] = int(Math.random()*(stage.stageWidth - 30));
loops--;
} while((loops>0)&&closetest());
}
This will force an exit from that loop if we tried 10 randoms and none satisfied - the last attempt is returned.