/* 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);
}