This site requires JavaScript, please enable it in your browser!
Greenfoot back
Celesterra
Celesterra wrote ...

2019/5/28

Transparency on Click

1
2
3
Celesterra Celesterra

2019/6/3

#
Yes, to a degree. The corner match can be made from the left most number (disregarding covers) of the top row and the right most number (also disregarding covers) of the bottom adjacent to the top row. I really like making things complicated, don't I? XD
Celesterra Celesterra

2019/6/3

#
Is there any possible way for the program to "scan" the numbers? Like a grid kind of thing that can detect what is a number, what is a cover, what is a highlighted number (don't worry about this one), the value of a number, and how many numbers there are?
danpost danpost

2019/6/4

#
Celesterra wrote...
Is there any possible way for the program to "scan" the numbers? Like a grid kind of thing that can detect what is a number, what is a cover, what is a highlighted number (don't worry about this one), the value of a number, and how many numbers there are?
Yes to all questions. Scanning would involve two for loops, each executed twice (for both directions). In each loop will be a while loop which will look for the first clicked Number object. Of course, all this is after (1) detecting click; (2) ensuring 2nd click; (3) not same Number object clicked; and (4) valid number with first clicked. See what you can do. Post any problem codes in the process with issue details.
Celesterra Celesterra

2019/6/4

#
What would the code be to find firstClicked? Would it be 'firstedClicked != null'?
Super_Hippo Super_Hippo

2019/6/4

#
I started programming the game today and it works pretty well so far* using an ArrayList to keep a reference to all Numbers in the World and an int variable to keep a reference to the index of the "first clicked" Number. So everything is handled from the World. The only thing the Numbers are doing is that they tell the world when they are clicked. *so far = displaying and removing numbers works, removing lines probably already works as well (didn't test yet). Next step is to re-add the numbers when nothing is possible anymore This is how the method currently looks which is called whenever a number is pressed. Maybe this gives you some idea how it could be done. (I did not include this bottom right back to top left check and probably will not.)
    public void pressedNumber(Number number)
    {
        if (activeNumber == -1)
        { //when there is no active number currently
            changeActive(number);
        }
        else if (number == numbers.get(activeNumber))
        { //active number was pressed again
            //do nothing
        }
        else if (number.getValue() != numbers.get(activeNumber).getValue()
            && number.getValue() + numbers.get(activeNumber).getValue() != 10)
        { //when the numbers are not the same and the sum isn't 10
            changeActive(number);
        }
        else
        { //both numbers "match" and could be removed
            int[] n = {activeNumber, numbers.indexOf(number)};
            boolean match = false;
            if (n[0]%9 == n[1]%9)
            {//both Numbers are in the same column
                match = true; //matched unless it is prevented by an unsolved number in between
                for (int i=n[0]/9; i != n[1]/9; i = i+(int)Math.signum(n[1]-n[0]))
                {
                    if (i==n[0]/9) continue;
                    
                    if (!numbers.get(i*9+n[0]%9).getSolved())
                    { //unsolved number in between
                        changeActive(number);
                        match = false;
                        break;
                    }
                }
            }
            
            if (!match)
            { //not matched yet – no need to check if a match was found with the columns already
                match = true; //matched unless it is prevented by an unsolved number in between
                for (int i=n[0]; i != n[1]; i = i+(int)Math.signum(n[1]-n[0]))
                {
                    if (i==n[0]) continue;
                    
                    if (!numbers.get(i).getSolved())
                    { //unsolved number in between
                        changeActive(number);
                        match = false;
                        break;
                    }
                }
            }
            
            if (match)
            {//if nothing unsolved is between them
                solve(number);
            }
        }
    }
danpost danpost

2019/6/4

#
I did it a different way. I had the Number objects do most of the work. The act method looks like this:
public void act()
{
    if (!Greenfoot.mouseClicked(this)) return; // do nothing if not clicked
    if (first == null) { meFirst(); return; } // when this is first clicked
    if (first == this) { notMe(); return; } // when this is clicked twice
    if (isValid() && inLine()) { covers(); return; } // when matching
}
which should be pretty straight-forward. The methods called are as follows:
/** when first to be clicked */
private void meFirst()
{
    first = this; // save first in static field
    getWorld().addObject(HILITE, getX(), getY()); // add highlight actor
}

/** when this is both first and second clicks */
private void notMe()
{
    first = null; // allow new first click
    getWorld().removeObject(HILITE); // remove highlight
}

/** check numbers */
private boolean isValid()
{
    return first.number == number || first.number+number == 8; // internal numbers are 1 less each than that displayed
}

/** check placement */
private boolean inLine()
{
    int n = getY()*9+getX(); // position in grid
    // horizontal check
    int dir = 1;
    int dn = dir;
    for (int i=0; i<2; i++)
    {
        dir = -dir; dn = dir; // so the two iterations go in opposite directions
        while (!getWorld().getObjectsAt(((n+dn+108)%108)%9, ((n+dn+108)%108)/9, Cover.class).isEmpty()) dn += dir; // skip past any covers
        if (!getWorld().getObjectsAt(((n+dn+108)%108)%9, ((n+dn+108)%108)/9, Number.class).contains(first)) continue; // check not first clicked
        return true;
    }
    // vertical check
    dir = 9;
    for (int i=0; i<2; i++)
    {
        dir = -dir; dn = dir; // so the two iterations go in opposite directions
        while (!getWorld().getObjectsAt(((n+dn+108)%108)%9, ((n+dn+108)%108)/9, Cover.class).isEmpty()) dn += dir; // skip past any covers
        if (!getWorld().getObjectsAt(((n+dn+108)%108)%9, ((n+dn+108)%108)/9, Number.class).contains(first)) continue; // check not first clicked
        return true;
    }
    return false;
}

/** for valid matches */
private void covers()
{
    getWorld().addObject(new Cover(), getX(), getY()); // cover this
    getWorld().addObject(new Cover(), first.getX(), first.getY()); // cover first clicked
    getWorld().removeObject(HILITE); // remove highlight
    first = null; // allow new first clicked
}
Some of the code might need to be re-written/refactored to allow the world to check that no matches are possible. In fact, I have an idea I am going to try which might help all around (although there are some drawbacks that come with it).
Super_Hippo Super_Hippo

2019/6/4

#
Interesting that you did it the other way. Checking if anything is possible was easier than expected to be honest: (at least with my attempt)
    public boolean possibleToRemoveNumbers()
    {
        for (int i=0; i<numbers.size(); i++)
        {
            if (numbers.get(i).getSolved()) continue; //solved field can't be matched again
            
            for (int j=i+1; j<numbers.size(); j++) //only need to check numbers after this one
            {
                if (numbers.get(j).getSolved()) continue; //solved field doesn't matter
                
                if (numbers.get(i).getValue() == numbers.get(j).getValue()
                    || numbers.get(i).getValue() + numbers.get(j).getValue() == 10)
                { //found a match
                    return true;
                }
                else break; //wrong number found
            }
            
            for (int j=i+9; j<numbers.size(); j+=9) //only need to check numbers below this one
            {
                if (numbers.get(j).getSolved()) continue; //solved field doesn't matter
                
                if (numbers.get(i).getValue() == numbers.get(j).getValue()
                    || numbers.get(i).getValue() + numbers.get(j).getValue() == 10)
                { //found a match
                    return true;
                }
                else break; //wrong number found
            }
        }
        
        return false; //nothing found
    }
Super_Hippo Super_Hippo

2019/6/4

#
danpost danpost

2019/6/6

#
I ended up with the following Number class, which keeps track of its 4 in-line neighbors (which any could end up to be itself) and whether they are an actual match; also, it tracks the state of one having any matches or not, so the total count of Number objects with valid matches can be tracked by the world. When, covering numbers, only those affected by them are re-assessed:
import greenfoot.*;

public class Number extends Actor
{
    public static HiLite HILITE;
    private static final Color[] COLORS =
    {
        Color.BLACK,
        new Color(255, 161, 38),
        new Color(163, 8, 3),
        new Color(8, 204, 11),
        new Color(37, 113, 234),
        new Color(8, 204, 11),
        new Color(163, 8, 3),
        new Color(255, 161, 38),
        Color.BLACK,
    };
    private static Number first;

    public int number;
    public Number[] pairs = new Number[4];
    public boolean[] matching = new boolean[4];
    private boolean hasMatch;

    public Number(int num)
    {
        number = num;
        GreenfootImage img = new GreenfootImage(45, 45);
        GreenfootImage numImg = new GreenfootImage(""+(num+1), 36, COLORS[num], new Color(0, 0, 0, 0));
        img.drawImage(numImg, 22-numImg.getWidth()/2, 22-numImg.getHeight()/2);
        setImage(img);
    }
    
    /** Checks for clicks and act upon them */
    public void act()
    {
        if (!Greenfoot.mouseClicked(this)) return;
        if (first == null) { meFirst(); return; }
        int index = java.util.Arrays.asList(pairs).indexOf(first);
        if (index < 0 || !matching[index]) { clearFirst(); return; }
        covers();
    }
    
    /** Sets first click number */
    private void meFirst()
    {
        first = this;
        getWorld().addObject(HILITE, getX(), getY());
    }
    
    /** Prepares for new first click */
    private void clearFirst()
    {
        first = null;
        getWorld().removeObject(HILITE);
    }
    
    /** Looks for number paired with along given direction */
    public void testDir(int d) // R=0, D=1, L=2, U=3
    {
        pairs[d] = null;
        matching[d] = false;
        int n = getX()+9*getY();
        int j = (d%2 == 0) ? 1 : 9;
        int dn = (d%2 == 0 ? 1-d : 2-d);
        // find next uncovered (even back to self)
        for (int i=1; i<=108/j; i++)
        {
            if (getWorld().getObjectsAt((n+i*j*dn+108)%9, ((n+i*j*dn+108)%108)/9, Cover.class).isEmpty())
            {
                pairs[d] = (Number)getWorld().getObjectsAt((n+i*j*dn+108)%9, ((n+i*j*dn+108)%108)/9, Number.class).get(0);
                break;
            }
        }
        // determine match
        if (pairs[d] != null && pairs[d] != this && (pairs[d].number == number || pairs[d].number+number == 8)) matching[d] = true;
        // check if state of having a match changed
        if (hasMatch != matchHeld())
        {
            hasMatch = !hasMatch;
            MyWorld.playerCount += (hasMatch ? 1 : -1);
        }
    }
    /** Returns current state of having a match */
    private boolean matchHeld()
    {
        return matching[0] || matching[1] || matching[2] || matching[3];
    }
    
    /** Covers a matching pair and updates pairings */
    private void covers()
    {
        getWorld().addObject(new Cover(), getX(), getY());
        getWorld().addObject(new Cover(), first.getX(), first.getY());
        MyWorld.playerCount -= 2;
        for (int n=0; n<4; n++)
        {
            if (pairs[n] != first && pairs[n] != this) pairs[n].testDir((n+2)%4);
            if (first.pairs[n] != this && first.pairs[n] != first) first.pairs[n].testDir((n+2)%4);
        }
        clearFirst();
    }
}
The world has all numbers initialize their pairing after adding all of them into the world.
You need to login to post a reply.
1
2
3