views:

298

answers:

6

I want to make a simple Go board to design an Computer Go game.

In a go game, you lie a "stone" (white or black) on a position where horizontal and vertical lines intersect.

What are some simple ways to restrict users from placing their stones in other locations?
Maybe I'm just not seeing a simple solution.

EDIT
I guess I should rephrase my question better: I want to know how to do the background image of Go board, so that I can lie my stones on the intersection of the horizontal and the vertical lines. I was thinking about getting a just regular Go board image, and when I'm actually rendering stones, I find right position of pixels to lie stones. However, that solution did not seem to be the best solution, since I need to worry about size of stone images and think about proportionality when I either expand or shrink the board window.

+1  A: 

Don't think about the lines. Think about the intersections. The intersections can be represented as a grid in the same way that the squares on a chess board form a grid.

In Java the simplest representation would be a square array. Something like:

Location[MAX_Y][MAX_X];

where Location is an object representing the intersection and holding a reference to the piece placed there (if any).

Brian Agnew
+1  A: 
int[][] grid = new int[19][19];

There’s your grid. Each location in this array represents a line intersection.

Bombe
+1  A: 

I'd use enums here:

enum Stone {BLAC, WHITE, NONE}

class Board {
    static final int dimension = 19;
    private Stone[][] board = new Stone[dimension][dimension];
    // ...
}

Edit:

Here's a small demo (no resizing of the board and no images, just good old Graphics!):

import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;

public class GoBoardDemo {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setLayout(new BorderLayout());
        frame.add(new GoPanel(19), BorderLayout.CENTER);
        frame.setSize(600, 625);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setVisible(true);
    }
}


@SuppressWarnings("serial")
class GoPanel extends JPanel {

    Square[][] board;
    boolean whiteToMove;

    GoPanel(int dimension) {
        board = new Square[dimension][dimension];
        whiteToMove = true;
        initBoard(dimension);
    }

    private void initBoard(int dimension) {
        super.setLayout(new GridLayout(dimension, dimension));
        for(int row = 0; row < dimension; row++) {
            for(int col = 0; col < dimension; col++) {
                board[row][col] = new Square(row, col);
                super.add(board[row][col]);
            }
        }
        repaint();
    }

    private class Square extends JPanel {

        Stone stone;
        final int row;
        final int col;

        Square(int r, int c) {
            stone = Stone.NONE;
            row = r;
            col = c;
            super.addMouseListener(new MouseAdapter(){
                @Override
                public void mouseClicked(MouseEvent me) {
                    if(stone != Stone.NONE) return;
                    stone = whiteToMove ? Stone.WHITE : Stone.BLACK;
                    whiteToMove = !whiteToMove;
                    repaint();
                }
            });
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            int w = super.getWidth();
            int h = super.getHeight();
            g.setColor(new Color(0xB78600));
            g.fillRect(0, 0, w, h);
            g.setColor(Color.BLACK);   
            if(row == 0 || row == board.length-1 || col == 0 || col == board.length-1) {
                if(col == 0) {
                    g.drawLine(w/2, h/2, w, h/2);
                    if(row == 0) g.drawLine(w/2, h/2, w/2, h);
                    else if(row == 18) g.drawLine(w/2, h/2, w/2, 0);
                    else g.drawLine(w/2, 0, w/2, h);
                }
                else if(col == 18) {
                    g.drawLine(0, h/2, w/2, h/2);
                    if(row == 0) g.drawLine(w/2, h/2, w/2, h);
                    else if(row == 18) g.drawLine(w/2, h/2, w/2, 0);
                    else g.drawLine(w/2, 0, w/2, h);
                }
                else if(row == 0) {
                    g.drawLine(0, h/2, w, h/2);
                    g.drawLine(w/2, h/2, w/2, h);
                }
                else {
                    g.drawLine(0, h/2, w, h/2);
                    g.drawLine(w/2, h/2, w/2, 0);
                }
            } else {
                g.drawLine(0, h/2, w, h/2);
                g.drawLine(w/2, 0, w/2, h);
            }
            stone.paint(g, w);
        }
    }
}

enum Stone { 

    BLACK(Color.BLACK), WHITE(Color.WHITE), NONE(null);

    final Color color;
    private final static Random rand = new Random();

    private Stone(Color c) {
        color = c;
    }

    public void paint(Graphics g, int dimension) {
        if(this == NONE) return;
        Graphics2D g2d = (Graphics2D)g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setColor(color);
        int x = 5;
        g2d.fillOval(rand.nextInt(x), rand.nextInt(x), dimension-x, dimension-x);
    }
}

The Random stuff in there is an implementation the organic feel CPerkins was talking about. Well, I tried to do it anyway.

Bart Kiers
AWESOME. exactly what I wanted to know. I should start learning more about Graphics. This is quite interesting.
codingbear
You're welcome.
Bart Kiers
+2  A: 

How to implement positions has been answered. But what you asked is how to limit users in their placement, which is a different thing.

Presumably, you're planning to create a drawing area, and you'll detect mouse clicks. How you'd limit stone placement to the intersections is like what drawing programs do to implement a feature called "snap" (as in "snap to grid").

Basically, that just means reducing the clicks which fall over a range of pixels in each dimension into being on one of the lines. Doing this in both dimensions puts the moved click at a point.

Essentially, what you're doing is to find the midpoint of each cell, and any click which falls between one cell midpoint and the next will be counted as being on the line between those midpoints.

So basically, this would be something like this:


    // Since traditionally, boards are not square, you'd call this twice: once with the
    //   width in X for the X click, and once again for Y.
    // Naturally, you'll want to accomodate e.g., 9x9 boards for short games.
    int snapClick (int gridWidth, int clickPos, int numCells) {
        int cellWidth = (int) (gridWidth / numCells);
        int snappedClick = Math.round ((clickPos + (cellWidth/2)) / cellWidth);
        return snappedClick;
    }

Incidentally, in actual games, stones aren't perfectly placed, so games have a pleasing organic feel. You might want to consider a solution in which you store not only the position of the stones in the grid, but also in some slightly-less-than-perfectly alignment on the screen, for display.

CPerkins
Ha, never thought about the *organic feel* of a game, but it makes perfect sense. Nice to know!
Bart Kiers
Although he didn't answer all my questions, this is really cool!
codingbear
A: 

If your primary goal is to implement the AI, they I suggest implementing the Go Text Protocol, and making use of one of the many existing GTP front ends. If you want to build the GUI as a learning experience then go ahead (no pun intended). As someone who wrote a commercial go GUI, I warn you, it is much more difficult than it seems (A chess app would be simple by comparison).

mikerobi
A: 

Hi guys

I'm a new programmer, but thought I would try and extend Bart K's implementation to have the rules of Go, but I've fallen at the 1st hurdle!

I'm trying to pass the co-ordinates of the square clicked using dispatchEvent to the board class, so that I can do a search for surrounding stones on the clicked panel.

Am I missing something simple using my method or is there just an easier way? Many thanks

 class GoPanel extends JPanel implements ActionListener{

...

public void actionPerformed(ActionEvent e){

    int r = Integer.parseInt(e.getActionCommand().substring(0,0));
    int c = Integer.parseInt(e.getActionCommand().substring(2,2));



    if(board[r][c].stone != Stone.NONE) return;
    board[r][c].stone = whiteToMove ? Stone.WHITE : Stone.BLACK;
    whiteToMove = !whiteToMove;


    repaint();





  public void mouseClicked(MouseEvent me) {

        String a = Integer.toString(row) + " " + Integer.toString(col); 
        dispatchEvent(new ActionEvent (this, AWTEvent.RESERVED_ID_MAX + 1, a));                 

   }  
CodeMonkey