> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.6pgteRGwEWY/rev.5911
 * 
 * authors: 
 *   Moshe Zihmih
 *   
 *   

 * license (unless otherwise specified): 
 *   creative commons attribution-share alike 3.0 license.
 *   https://creativecommons.org/licenses/by-sa/3.0/ 
 */ 




// Game parameters:
size(500, 520);
int board_size = 9;     // width and height of the Go board
int player_count = 2;    // number of participating players
String suicide_msg = "You are suiciding.";
String pointtaken_msg = "This point is already taken.";
String nextplayer_msg = "";
String prisoners_msg = "";
boolean debug = false;

Margin margin = new Margin(65, 65, 65, 95);
int current_player = 1;  // starting player
Group[][] board;
Group[][] board_old; // keeps old state for Ko rule
int dw, dh; // distance between board lines
boolean pass_flag = false;
Button pass_btn;

class Margin
{
    int left, right, top, bottom;
    
    Margin(int l, int r, int t, int b)
    {
        left = l;
        right = r;
        top = t;
        bottom = b;
    }
}

class Button
{
    string text_;
    int x, y, w, h;
    
    Button(string t, int x, int y, int w, int h)
    {
        this.text_ = t;
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
    }
    
    void draw()
    {
        fill(80);
        rectMode(CENTER);
        rect(x, y, w, h, 5);
        
        fill(255);
        text(text_, x, y);
    }
    
    boolean isMouseOver(int mx, int my)
    {
        boolean bx = mx >= (x - w / 2) && mx <= (x + w / 2);
        boolean by = my >= (y - h / 2) && my <= (y + h / 2);
        return bx && by;
    }
}

class Group
{
    int player;
    ArrayList stones; // stones are represented by `PVector`s
    ArrayList liberties; // all unique liberties around the group
    
    // creates a new group for the given player
    Group(int p)
    {
        player = p;
        stones = new ArrayList();
        liberties = new ArrayList();
    }
    
    int countLiberties()
    {
        return liberties.size();
    }
    
    void addLiberty(int r, int c)
    {
        PVector vec = new PVector(r, c);
        for (int i = 0; i < liberties.size(); i++)
        {
            PVector vec2 = liberties.get(i);
            if (vec.x == vec2.x && vec.y == vec2.y)
            {
                return;
            }
        }
        liberties.add(vec);
    }
    
    void add(int r, int c)
    {
        stones.add(new PVector(r, c));
        surround(r, c); // remove the liberty this stone was placed on
        // add liberties
        if (r > 0 && board[r-1][c].player == 0)
        {
            addLiberty(r-1, c);
        }
        if (r < board_size - 1 && board[r+1][c].player == 0)
        {
            addLiberty(r+1, c);
        }
        if (c > 0 && board[r][c-1].player == 0)
        {
            addLiberty(r, c-1);
        }
        if (c < board_size - 1 && board[r][c+1].player == 0)
        {
            addLiberty(r, c+1);
        }
    }
    
    // updates liberties for 
    void updateLiberties(int r, int c)
    {
    }
    
    int capture() // mark group as empty and update nearby liberties
    {
        println(stones.size() + " stones of player " + player + " were captured!");
        player = 0;
        liberties.clear();
        return stones.size();
    }
    
    void surround(int r, int c)
    {
        PVector vec = new PVector(r, c);
        for (int i = 0; i < liberties.size(); i++)
        {
            PVector vec2 = liberties.get(i);
            if (vec.x == vec2.x && vec.y == vec2.y)
            {
                liberties.remove(i);
                break;
            }
        }
    }
}

void setup() { 
    
    // set the background color
    background(255);
    
    // canvas size (Integers only, please.)
    //size(500, 520);
      
    // smooth edges
    //smooth();
    
    // limit the number of frames per second
    frameRate(5);
    
    // set the width of the line. 
    strokeWeight(2);
    
    textMode(SCREEN);
    textAlign(CENTER, CENTER);
    
    init();
} 

void init()
{
    pass_btn = new Button("Pass", width - 40, height - 30, 50, 25);
    
    dw = (width - margin.left - margin.right) / (board_size - 1);
    dh = (height - margin.top - margin.bottom) / (board_size - 1);
    
    board = new Group[board_size][board_size];
    board_old = new Group[board_size][board_size];
    
    Group empty = new Group(0);
    // set the group on all the points
    for (int i = 0; i < board_size; i++)
    {
        for (int j = 0; j < board_size; j++)
        {
            board[i][j] = empty;
            board_old[i][j] = empty;
        }
    }
    // add all the points to the group once all points are filled
    for (int i = 0; i < board_size; i++)
    {
        for (int j = 0; j < board_size; j++)
        {
            empty.add(i, j);
        }
    }
}

void drawBoard()
{
    stroke(0, 0, 0);
    fill(0);
    
    // draw rows and columns
    for (int i = 0; i < board_size; i++)
    {
        line(margin.left, margin.top + dh * i, width - margin.right, margin.top + dh * i); // row
        text(i + 1, margin.left - 35, margin.top + dh * i); // row number
        text(i + 1, width - margin.right + 35, margin.top + dh * i); // row number
        
        line(margin.left + dw * i, margin.top, margin.left + dw * i, height - margin.bottom); // column
        text(char(i + 'A'), margin.left + dw * i, margin.top - 35); // column letter
        text(char(i + 'A'), margin.left + dw * i, height - margin.bottom + 35); // column letter
    }
}

void draw() {
    background(255); // clear the canvas
    
    drawBoard();
        
    // draw pieces
    for (int i = 0; i < board_size; i++)
    {
        for (int j = 0; j < board_size; j++)
        {
            color c;
            switch (board[i][j].player)
            {
                case 0: continue;
                case 1: c = color(0, 0, 0); break;
                case 2: c = color(255, 255, 255); break;
                case 3: c = color(255, 0, 0); break;
                case 4: c = color(0, 255, 0); break;
                case 5: c = color(0, 0, 255); break;
            }
            fill(c);
            ellipse(margin.left + dw * j, margin.top + dh * i, 0.9 * dw, 0.9 * dh);
        }
    }
    
    pass_btn.draw();
}

void move(int r, int c)
{
    if (r < 0 || r > board_size - 1 ||
        c < 0 || c > board_size - 1)
    {
        return; // not in board range
    }
    else if (board[r][c].player != 0) // point taken
    {
        println(pointtaken_msg);
    }
    else // point is empty
    {
        Group[] adj_groups = {
            (r > 0              ? board[r-1][c] : null),
            (r < board_size - 1 ? board[r+1][c] : null),
            (c > 0              ? board[r][c-1] : null),
            (c < board_size - 1 ? board[r][c+1] : null),
        };
        
        boolean isSurrounded = true;
        for (int i = 0; i < 4; i++)
        {
            if (adj_groups[i] != null &&
                (adj_groups[i].player == 0 ||
                 adj_groups[i].player == current_player))
            {
                isSurrounded = false;
                break;
            }
        }
        
        if (isSurrounded)
        {
            println(suicide_msg);
        }
        else // empty spot, or friendlies
        {
            boolean placed = false;
            // place and capture
            for (int i = 0; i < 4; i++)
            {
                Group group = adj_groups[i];
                if (group != null)
                {
                    if (group.player == current_player)
                    {
                        group.add(r, c); // add the point to the group
                        board_old[r][c] = board[r][c]; // update previous state
                        board[r][c] = group; // update the current state
                        placed = true;
                    }
                    else if (group.countLiberties() == 1) // last empty spot
                    {
                        int g = group.capture(); // removes from the board
                    }
                    else
                    {
                        group.surround(r, c); // removes a liberty
                    }
                }
            }
            
            if (!placed)
            {
                board_old[r][c] = board[r][c]; // update previous state
                board[r][c] = new Group(current_player); // update the current state
                board[r][c].add(r, c);
            }
            
            // TODO: Ko rule
        
            pass_flag = false;
            next_player();
        }
    }
}

void next_player()
{
    current_player++;
    if (current_player > player_count)
        current_player = 1;
    
    if (debug)
        println("Player " + current_player + "'s turn...");
}

void pass()
{
    if (pass_flag)
    {
        // game end
    }
    else {
        pass_flag = true;
        next_player();
        println("Pass to end the game");
    }
}

void mousePressed()
{
    int column = round((mouseX - margin.left) / dw);
    int row = round((mouseY - margin.top) / dh);
    
    if (pass_btn.isMouseOver(mouseX, mouseY))
        pass();
    else
        move(row, column);
}