Search code examples
javacanvasviewgridminesweeper

How to know which cell of a grid (10x10) I've touched?


I'm creating a minesweeper using Canvas and a grid 10x10 I've drawn using canvas.drawRect. The problem is, I don't know HOW to get any cell's coordinates (for example : in the position (2,3) on my grid, what are the physical coordinates ?). Maybe i'm not doing the grid correctly ? ( I'm a beginner.. ) But I have to use Canvas, no Graphics. I saw some answers here but nothing that really helped me..

Here's the code :

package com.example.meinsweeper_ja;

import java.util.Random;
import android.annotation.SuppressLint;
import android.content.*;
import android.graphics.*;
import android.util.*;
import android.view.*;

public class CustomView extends View {

private Paint black, grey, white, blue, green, yellow, red;
private Rect square, edge;
private boolean grid[][]; // true = uncovered false = covered
private boolean mines[][]; // true = mine false = no mine
private int arround[][]; // Nb of mine around
private float touchx[];
private float touchy[];
private int nb_mines, nb_ard;

public CustomView(Context c) {
    super(c);
    init();
}

public CustomView(Context c, AttributeSet as) {
    super(c, as);
    init();
}

public CustomView(Context c, AttributeSet as, int default_style) {
    super(c, as, default_style);
    init();
}

private void init() {
    black = new Paint(Paint.ANTI_ALIAS_FLAG);
    grey = new Paint(Paint.ANTI_ALIAS_FLAG);
    white = new Paint(Paint.ANTI_ALIAS_FLAG);
    yellow = new Paint(Paint.ANTI_ALIAS_FLAG);
    red = new Paint(Paint.ANTI_ALIAS_FLAG);
    green = new Paint(Paint.ANTI_ALIAS_FLAG);
    blue = new Paint(Paint.ANTI_ALIAS_FLAG);

    black.setColor(0xFF000000);
    grey.setColor(0xFF808080);
    white.setColor(0xFFFFFFFF);
    yellow.setColor(0xFFFFFF00);
    red.setColor(0xFFFF0000);
    green.setColor(0xFF00FF00);
    blue.setColor(0xFF0000FF);

    touchx = new float[10];
    touchy = new float[10];
    grid = new boolean[10][10];
    mines = new boolean[10][10];

    arround = new int[10][10];

    nb_mines = 20;

    square = new Rect(-24, -24, 24, 24);
    edge = new Rect(100, 100, 100, 100);

    reset();
}

private void reset() {
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 10; j++) {
            grid[i][j] = false; // CLEAR THE BOARD
            if (nb_mines > 0) { // PLACE 20 MINES
                Random ping = new Random();
                if ((ping.nextInt(4) == 1)) {
                    mines[i][j] = true;
                    nb_mines--;
                }
            }
        }
    }

    for (int i = 0; i < 10; i++) { // CALCULATES NB OF MINES AROUND
        for (int j = 0; j < 10; j++) {
            if (!mines[i][j]) {
                nb_ard = 0;
                if (i == 0 && j == 0) { // TOP LEFT
                    if (mines[0][1])
                        nb_ard++;
                    if (mines[1][0])
                        nb_ard++;
                    if (mines[1][1])
                        nb_ard++;
                    arround[i][j] = nb_ard;
                } else if (i == 0 && j > 0 && j < 9) { // 1st Column
                    if (mines[0][j - 1])
                        nb_ard++;
                    if (mines[0][j + 1])
                        nb_ard++;
                    if (mines[1][j])
                        nb_ard++;
                    if (mines[1][j - 1])
                        nb_ard++;
                    if (mines[1][j + 1])
                        nb_ard++;
                    arround[i][j] = nb_ard;
                } else if (j == 0 && i < 9) { // 1st Row
                    if (mines[i - 1][0])
                        nb_ard++;
                    if (mines[i + 1][0])
                        nb_ard++;
                    if (mines[i][1])
                        nb_ard++;
                    if (mines[i - 1][1])
                        nb_ard++;
                    if (mines[i + 1][1])
                        nb_ard++;
                    arround[i][j] = nb_ard;
                } else if (i == 9 && j == 9) { // BOTTOM RIGHT
                    if (mines[8][8])
                        nb_ard++;
                    if (mines[8][9])
                        nb_ard++;
                    if (mines[9][8])
                        nb_ard++;
                    arround[i][j] = nb_ard;
                } else if (i == 9 && j < 9 && j > 0) { // Last Column
                    if (mines[9][j - 1])
                        nb_ard++;
                    if (mines[9][j + 1])
                        nb_ard++;
                    if (mines[8][j])
                        nb_ard++;
                    if (mines[8][j - 1])
                        nb_ard++;
                    if (mines[8][j + 1])
                        nb_ard++;
                    arround[i][j] = nb_ard;
                } else if (j == 9 && i < 9 && i > 0) { // Last Row
                    if (mines[i - 1][0])
                        nb_ard++;
                    if (mines[i + 1][0])
                        nb_ard++;
                    if (mines[i][8])
                        nb_ard++;
                    if (mines[i - 1][8])
                        nb_ard++;
                    if (mines[i + 1][8])
                        nb_ard++;
                    arround[i][j] = nb_ard;
                } else if (i == 0 && j == 9) { // BOTTOM LEFT
                    if (mines[0][8])
                        nb_ard++;
                    if (mines[1][9])
                        nb_ard++;
                    if (mines[1][8])
                        nb_ard++;
                    arround[i][j] = nb_ard;
                } else if (i == 9 && j == 0) { // TOP RIGHT
                    if (mines[9][1])
                        nb_ard++;
                    if (mines[8][0])
                        nb_ard++;
                    if (mines[8][1])
                        nb_ard++;
                    arround[i][j] = nb_ard;
                } else { // MIDDLE GRID
                    if (mines[i - 1][j - 1])
                        nb_ard++;
                    if (mines[i - 1][j])
                        nb_ard++;
                    if (mines[i - 1][j + 1])
                        nb_ard++;
                    if (mines[i][j - 1])
                        nb_ard++;
                    if (mines[i][j + 1])
                        nb_ard++;
                    if (mines[i + 1][j - 1])
                        nb_ard++;
                    if (mines[i + 1][j])
                        nb_ard++;
                    if (mines[i + 1][j + 1])
                        nb_ard++;
                    arround[i][j] = nb_ard;
                }
            }
        }
    }

}

@Override
public void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 10; j++) {
            touchy[j] = 50.f;
            canvas.save();
            if (i == 0 && j == 0)
                canvas.translate(200.f, touchy[j]);
            else
                canvas.translate(touchx[i], touchy[j]);
            canvas.drawRect(edge, white);

            if (!grid[i][j])
                canvas.drawRect(square, black);
            else {
                if (arround[i][j] == 1) {
                    canvas.drawRect(square, grey);
                    canvas.drawText(Integer.toString(1), i, j, blue);
                } else if (arround[i][j] == 2) {
                    canvas.drawRect(square, grey);
                    canvas.drawText(Integer.toString(2), i, j, green);
                } else if (arround[i][j] == 3) {
                    canvas.drawRect(square, grey);
                    canvas.drawText(Integer.toString(3), i, j, yellow);
                } else if (arround[i][j] > 3) {
                    canvas.drawRect(square, grey);
                    canvas.drawText(Integer.toString(arround[i][j]), i, j,
                            red);
                } else if (mines[i][j]) {
                    canvas.drawRect(square, red);
                    canvas.drawText("M", i, j, black);
                } else if (arround[i][j] == 0)
                    canvas.drawRect(square, grey);
            }
        }
        touchx[i] += 50.f;
        canvas.translate(touchx[i], -10 * touchx[i]);
    }
}

@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
    if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
        int pidX = (int) event.getX();
        int pidY = (int) event.getY();
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 10; j++) {
                if (touchx[i] == pidX && touchy[j] == pidY) {
                    if (mines[i][j]) {
                        reset();
                    }
                }
            }
        }
        invalidate();
        return true;
    }
    return super.onTouchEvent(event);
}
}

The issue is surely in the onDraw() method but as I said before, I don't really know how to draw grid in a better way.. I hope you understand my issue..

I know I have large methods, I'll fix this when I'll find any solution to my problem.

Thanks in advance !


Solution

  • So you might consider refactoring your code...

    Instead of having different objects, create an object like "Tile". It could look something like: public enum DrawState { Normal, Safe, Exploded; }
    public class Paints { public static Paint NORMAL, SAFE, EXPLODED; static { NORMAL = new Paint(); NORMAL.setColor(...) setStyle(...) // etc } // these can even be members of the DrawState just pass them into the // constructor then you can do like DrawState.Exploded.getPaint(); }
    public class Tile { private final int id; private Rect boundingRect; private boolean hasMine; private DrawState drawState; public Tile(int theId, Rect theBoundingRect, boolean mined) { id = theId; boundingRect = theBoundingRect; hasMine = mined; drawState = DrawState.Normal; }
    public boolean containsTouch(int x, int y) { return boundingRect.contains(x, y); }
    public void draw(Canvas c) { switch (state) { case Normal: c.drawRect(boundingRect, Paints.NORMAL); return; case Safe: c.drawRect(boundingRect, Paints.SAFE); return; case Exploded: c.drawRect(boundingRect, Paints.EXPLODED); } }
    public void setInspected() { drawState = DrawState.SAFE; }
    public boolean isMined() { return hasMine; } public int getId() { return id; } // etc }

    Now in your custom field view, you can do things like:

    @Override public boolean onTouch(MotionEvent event) { for (Tile t : tiles) { if (t.containsTouch(event.getX(), event.getY())) { if (t.isMined()) { reset(); } else { t.setInspected(); } invalidate(); } } }

    Which makes things much simpler. Of course, you'll have to initialize your list of tiles, and you can do so by calculating the left, top, right, bottom of each rect and just passing in a new Rect(l, t, r, b) into the tile constructor. Then it makes it fairly easy to get the id, or array position, and a lot of other things...

    Of course, if you want the id as a tuple, you can just use the modulus operator; you don't even need to save id if you know your tiles are going to stay in the list at their initial index position. Similarly, you can also pass in i and j when you are initializing the tiles instead of an id.

    Hope this helps!

    Edited for further explanation:

    So now since each tile has enough information to take care of itself, it can have a draw function such as:
    public void draw(Canvas c) { c.drawRect(boundingRect, drawState.getPaint()); // assuming the paint is set to Style.FILL it'll fill the // rect with t/l/b/r as was set during construction // if you had a flag image, you could draw it on top of the boundingRect // int t = boundingRect.centerX() - (flagImage.getIntrisnicWidth()/2); // int l = boundingRect.centerY() - (flagImage.getIntrisnicHeight()/2); // c.drawBitmap(flagImage, bitmapTop, bitmapLeft, null); }

    And now from your custom grid view class: @Override public void onDraw(Canvas c) { // draw other things for (Tile t : tiles) { t.draw(c); } // draw other things }

    Remember what is drawn first will be drawn over, so if you do: canvas.drawRect(rect1); canvas.drawRect(rect2); And rect2 is in the same place as rect1 it'll draw it over rect1.