#define EMPTY	0
#define BLACK	1
#define WHITE	2

int board, mark, num_pass;

object black, white;
int to_play;

short() {
    return "board";
}

long() {
    write("A go board. If you want to play\n");
    write("with someone, do 'start name' with the name of your friend.\n");
    write("The player issuing the 'start' command will get black.\n");
    write("An optional numeric argument to start is the number of handicap stones.\n");
    if (black || white) {
	write("\n");
	display(board);
    }
}

id(str) {
    return str == "board" || str == "go board";
}

query_value() { return 10; }

get() {
    return !occupied();
}

reset(arg) {
    if (arg)
	return arg;
    to_play = EMPTY;
}

start(str) {
    int i, handicap;
    string name;
    if (environment() == this_player()) {
	write("You mus put it down first !\n");
	return 1;
    }
    if (sscanf(str, "%s %d", name, handicap) != 2)
	name = str;
    name = present(name, environment(this_object()));
    if (!name)
	return 0;
    if (occupied())
	return 1;
/*
    if (name == this_player()) {
	write("Sorry, you can't play against yourself.\n");
	return 1;
    }
*/
    notify(" starts a game with " + call_other(name, "query_name") + ".\n");
    to_play = BLACK;
    num_pass = 0;
    white = name;
    black = this_player();
    board = allocate(9);
    mark = allocate(9);
    while(i<9) {
	board[i] = allocate(9);
	mark[i] = allocate(9);
	i += 1;
    }
    write("Board initialized.\n");
    if (handicap) {
	if (handicap < 2 || handicap > 9) {
	    write("The handicap must be from 2 to 9 stones.\n");
	    white = 0;
	    black = 0;
	    return 1;
	}
	write("You get " + handicap + " stones, and white starts.\n");
	notify(" gets " + handicap + " stones, and white starts.\n");
	place_hand(handicap);
	to_play = WHITE;
    }
    return 1;
}

display(b) {
    int x, y;

    if (to_play == BLACK)
	write("Black '@' (" + black->query_name() + ") to play.\n\n");
    else
	write("White 'O' (" + white->query_name() + ") to play.\n\n");
    write("  0 1 2 3 4 5 6 7 8\n");
    while(x<9) {
	write(x + " ");
	while(y<9) {
	    if (b[x][y] == EMPTY)
		write(". ");
	    else if (b[x][y] == BLACK)
		write("@ ");
	    else if (b[x][y] == WHITE)
		write("O ");
	    y += 1;
	}
	write("\n");
	x += 1;
	y = 0;
    }
}

disp() {
    display(board);
    return 1;
}

init() {
    add_action("disp", "disp");
    add_action("start", "start");
    add_action("patch", "patch");
    add_action("play", "play");
    add_action("score", "score");
    add_action("fill", "fill");
}

count_lib(b, x, y) {
    clean(mark);
    return lib(b, mark, x, y);
}

clean(b) {
    int i, j;

    i = 0;
    while(i<9) {
	j = 0;
	while(j<9) {
	    b[i][j] = 0;
	    j += 1;
	}
	i += 1;
    }
}

lib(b, m, x, y) {
    int col, sum;

    sum = 0;
    col = b[x][y];
    m[x][y] = 1;
    if (x > 0 && !m[x-1][y]) {
	if (b[x-1][y] == col)
	    sum += lib(b, m, x-1, y);
	else if (b[x-1][y] == EMPTY) {
	    sum += 1;
	    m[x-1][y] = 1;
	}
    }
    if (y > 0 && !m[x][y-1]) {
	if (b[x][y-1] == col)
	    sum += lib(b, m, x, y-1);
	else if (b[x][y-1] == EMPTY) {
	    sum += 1;
	    m[x][y-1] = 1;
	}
    }
    if (x < 8 && !m[x+1][y]) {
	if (b[x+1][y] == col)
	    sum += lib(b, m, x+1, y);
	else if (b[x+1][y] == EMPTY) {
	    sum += 1;
	    m[x+1][y] = 1;
	}
    }
    if (y < 8 && !m[x][y+1]) {
	if (b[x][y+1] == col)
	    sum += lib(b, m, x, y+1);
	else if (b[x][y+1] == EMPTY) {
	    sum += 1;
	    m[x][y+1] = 1;
	}
    }
    return sum;
}

patch(str) {
    int x, y, col;
    int ret;

    if (!str)
	return 0;
    if (sscanf(str, "%d %d %s", x, y, col) != 3)
	return 0;
    if (col == "@")
	col = BLACK;
    else if (col == "O")
	col = WHITE;
    else if (col == ".")
	col = EMPTY;
    else
	return 0;
    if (col != EMPTY) {
	ret = enter(board, x, y, col);
	board[x][y] = col;
    } else
	board[x][y] = EMPTY;
    write("Ok.\n");
    return 1;
}

rem_stones(b, x, y) {
    int col, sum;

    col = b[x][y];
    b[x][y] = EMPTY;
    sum = 1;
    if (x>0 && b[x-1][y] == col)
	sum += rem_stones(b, x-1, y);
    if (y>0 && b[x][y-1] == col)
	sum += rem_stones(b, x, y-1);
    if (x<8 && b[x+1][y] == col)
	sum += rem_stones(b, x+1, y);
    if (y<8 && b[x][y+1] == col)
	sum += rem_stones(b, x, y+1);
    return sum;
}

enter(b, x, y, col) {
    int ocol, sum;
    b[x][y] = col;
    if (col == BLACK)
	ocol = WHITE;
    else
	ocol = BLACK;
    if (x>0 && b[x-1][y] == ocol && count_lib(b, x-1, y) == 0)
	sum += rem_stones(b, x-1, y);
    if (y>0 && b[x][y-1] == ocol && count_lib(b, x, y-1) == 0)
	sum += rem_stones(b, x, y-1);
    if (x<8 && b[x+1][y] == ocol && count_lib(b, x+1, y) == 0)
	sum += rem_stones(b, x+1, y);
    if (y<8 && b[x][y+1] == ocol && count_lib(b, x, y+1) == 0)
	sum += rem_stones(b, x, y+1);
    return sum;
}

play(str) {
    int x, y;
    int ret;

    if (!str)
	return 0;
    if (to_play == EMPTY) {
	write("You have to start the game with an opponent first.\n");
	return 1;
    }
    if (to_play == BLACK && this_player() != black) {
	write("It is not your turn now.\n");
	notify("tried (illegaly) to play.\n");
	return 1;
    }
    if (to_play == WHITE && this_player() != white) {
	write("It is not your turn now.\n");
	notify("tried (illegaly) to play.\n");
	return 1;
    }
    if (str == "pass") {
	num_pass += 1;
	notify("pass.\n");
	if (num_pass == 2) {
	    write("Game over.\n");
	    say("Game over.\n");
	    to_play = EMPTY;
	    return 1;
	}
	if (to_play == BLACK)
	    to_play = WHITE;
	else
	    to_play = BLACK;
	write("Ok.\n");
	return 1;
    }
    num_pass = 0;
    if (sscanf(str, "%d %d", x, y) != 2)
	return 0;
    notify("playes at " + x + " " + y + ".\n");
    ret = enter(board, x, y, to_play);
    if (ret) {
	write(ret + " stones removed, ");
	say("And captures " + ret + " stones.\n");
    }
    write(count_lib(board, x, y) + " liberties.\n");
    if (to_play == BLACK)
	to_play = WHITE;
    else
	to_play = BLACK;
    return 1;
}

count_score(b) {
    int x, y;
    int score;
    int col, tmp;

    clean(mark);
    while(x<9) {
	y = 0;
	while(y<9) {
	    col = b[x][y];
	    if (col == EMPTY) {
		y += 1;
		continue;
	    }
	    if (col == BLACK)
		score += 1;
	    else
		score -= 1;
	    tmp = count_spaces(b, x-1, y, col);
	    if (tmp < 1000 && tmp > -1000)
		score += tmp;
	    tmp = count_spaces(b, x+1, y, col);
	    if (tmp < 1000 && tmp > -1000)
		score += tmp;
	    tmp = count_spaces(b, x, y-1, col);
	    if (tmp < 1000 && tmp > -1000)
		score += tmp;
	    tmp = count_spaces(b, x, y+1, col);
	    if (tmp < 1000 && tmp > -1000)
		score += tmp;
	    y += 1;
	}
	x += 1;
    }
    return score;
}

chose_random() {
    int x, y, n;
    while(n < 10) {
	x = random(9);
	y = random(9);
	if (board[x][y] == EMPTY) {
	    int ret;
	    board[x][y] = BLACK;
	    if (count_lib(board, x, y) == 0) {
		n += 1;
		continue;
	    }
	    n = enter(board, x, y, BLACK);
	    write("I play on " + x + " " + y);
	    if (ret)
		write("And removes " + ret + " stones!");
	    write("\n");
	    return;
	}
	n += 1;
    }
    write("I pass.\n");
}

notify(str) {
    say(this_player()->query_name() + " " + str);
}

count_spaces(b, x, y, col)
{
    int tot;
    tot = count_spaces2(b, x, y, col);
    if (col == WHITE)
	tot = -tot;
    return tot;
}

count_spaces2(b, x, y, col)
{
    int tot;

    if (x < 0 || x > 8 || y < 0 || y > 8)
	return 0;
    if (b[x][y] == EMPTY) {
	if (mark[x][y])
	    return 0;
	mark[x][y] = 1;
	tot = 1;
    } else if (b[x][y] == col)
	return 0;
    else
	return 2000;
    write(x + " " + y + "\n");
    tot += count_spaces2(b, x-1, y, col);
    tot += count_spaces2(b, x+1, y, col);
    tot += count_spaces2(b, x, y-1, col);
    tot += count_spaces2(b, x, y+1, col);
    return tot;
}

score() {
    int tmp;

    write("The score is now ");
    tmp = count_score(board);
    if (tmp == 0)
	write("even.\n");
    else if (tmp > 0)
	write(tmp + " points to black.\n");
    else
	write(-tmp + " points to white.\n");
    return 1;
}

occupied() {
    if (to_play == EMPTY)
	return 0;
    if (black && present(black, environment(this_object()))) {
	write("You have to wait for " + call_other(black, "query_name") +
	      ".\n");
	return 1;
    }
    if (white && present(white, environment(this_object()))) {
	write("You have to wait for " + call_other(white, "query_name") +
	      ".\n");
	return 1;
    }
    return 0;
}

place_hand(n) {
    board[2][2] = BLACK;
    board[6][6] = BLACK;
    if (n < 3)
	return;
    board[2][6] = BLACK;
    if (n < 4)
	return;
    board[6][2] = BLACK;
    if (n < 5)
	return;
    board[4][4] = BLACK;
    if (n < 6)
	return;
    board[4][4] = EMPTY;
    board[4][2] = BLACK;
    board[4][6] = BLACK;
    if (n < 7)
	return;
    board[4][4] = BLACK;
    if (n < 8)
	return;
    board[4][4] = EMPTY;
    board[2][4] = BLACK;
    board[6][4] = BLACK;
    if (n < 9)
	return;
    board[4][4] = BLACK;
}

fill() {
    int x, y;
    int tot, b;
    b = board;
    board = allocate(9);
    while(x<9) {
	board[x] = allocate(9);
	x += 1;
    }
    x = 0;
    while(x<7) {
	x += 1;
	y = 0;
	while(y<7) {
	    int col;
	    y += 1;
	    col = b[x][y];
	    board[x][y] = col;
	    if (col == EMPTY)
		continue;
	    tot += fill_point(b, x-1, y, col);
	    tot += fill_point(b, x, y-1, col);
	    tot += fill_point(b, x, y+1, col);
	    tot += fill_point(b, x+1, y, col);
	}
    }
    if (tot > 0)
	write(tot + " spaces filled up.\n");
    else
	write("No more free spaces.\n");
    return 1;
}

fill_point(b, x, y, col)
{
    if (board[x][y] != EMPTY)
	return 0;
    if (ch(b,x-1,y,col) && ch(b,x,y-1,col) &&
	ch(b,x+1,y,col) && ch(b,x,y+1,col)) {
	board[x][y] = col;
	return 1;
    }
    return 0;
}

ch(b, x, y, col) {
    if (x < 0 || x > 8 || y < 0 || y > 8)
	return 1;
    if (b[x][y] == EMPTY || b[x][y] == col)
	return 1;
    return 0;
}
