// Prototyp arbitra do gry w okrêty
// wersja 0.4
//
// Autor: Arkadiusz Paterek <paterek@mimuw.edu.pl>
// Poprawki: Adam Iwanicki

#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>

#define FOR(i, n) for (int i = 0; i < (n); ++i)

#define BUF_SIZE 1024

#define ROUNDS 100
#define TIME 300
#define S_ROUNDS "100"
#define S_TIME "300"

typedef int Board[8][8];

struct Move {
	int x, y;
};

char board_string[2][BUF_SIZE];
Board board[2];
int ships[2];
FILE *input[2];
FILE *output[2];

#define syserr(fmt, args...) do { \
	fprintf(stderr, "Blad: " fmt ": %s\n", ##args, strerror(errno)); \
	exit(-1); \
} while (0)

#define error(fmt, args...) do { \
	fprintf(stderr, "Blad: " fmt "\n", ##args); \
	exit(-1); \
} while (0)

void getMessage(int player, char *buf, int buf_size) {
	assert(player == 0 || player == 1);

	int res = fscanf(output[player], " \(%[^)])", buf);
	fprintf(stderr, "<-%d: \"%s\" %d\n", player, buf, res);
}

void sendMessage(int player, char *buf) {
	assert(player == 0 || player == 1);

	fprintf(input[player], "%s\n", buf);
	fflush(input[player]);
	fprintf(stderr, "->%d: \"%s\"\n", player, buf);
}

inline int opponent(int player) {
	assert(player == 0 || player == 1);

	return 1 - player;
}

int sendStart(int player, int start_player, int round) {
	assert(player == 0 || player == 1);

	char buf[BUF_SIZE];
	sprintf(buf, "(%d,%d)", round, start_player == player ? 1 : 2);
	sendMessage(player, buf);
	return 1;
}

int iabs(int a) {
	return a >= 0 ? a : -a;
}

int fieldEmpty(Board &board, int xa, int ya) {
	if (xa < 0 || xa > 7 || ya < 0 || ya > 7)
		return 1;
	if (board[xa][ya])
		return 0;
	return 1;
}

int fieldEmptyExcept(Board &board, int xa, int ya, int xb, int yb) {
	if (xa == xb && ya == yb)
		return 1;
	return fieldEmpty(board, xa, ya);
}

int fieldEmptyExcept2(Board &board, int xa, int ya, int xb, int yb, int xc, int yc) {
	if (xa == xb && ya == yb || xa == xc && ya == yc)
		return 1;
	return fieldEmpty(board, xa, ya);
}

int checkShip1(Board &board, int x0, int y0) {
	int dx[] = {-1,  0,  1, -1, 1, -1, 0, 1};
	int dy[] = {-1, -1, -1,  0, 0,  1, 1, 1};
	FOR(i, 8)
		if (!fieldEmpty(board, x0 + dx[i], y0 + dy[i]))
			return 0;
	return 1;
}

int checkShip2(Board &board, int x0, int y0, int x1, int y1) {
	if (iabs(x1 - x0) + iabs(y1 - y0) != 1)
		return 0;
	int dx[] = {-1,  0,  1, -1, 1, -1, 0, 1};
	int dy[] = {-1, -1, -1,  0, 0,  1, 1, 1};
	FOR(i, 8)
		if (!fieldEmptyExcept(board, x0 + dx[i], y0 + dy[i], x1, y1))
			return 0;
	FOR(i, 8)
		if (!fieldEmptyExcept(board, x1 + dx[i], y1 + dy[i], x0, y0))
			return 0;
	return 1;
}

int checkShip3(Board &board, int x0, int y0, int x1, int y1, int x2, int y2) {
	if (iabs(x1 - x0) + iabs(y1 - y0) != 1)
		return 0;
	if (iabs(x2 - x1) + iabs(y2 - y1) != 1)
		return 0;
	if (x2 - x1 != x1 - x0 || y2 - y1 != y1 - y0)
		return 0;
	int dx[] = {-1,  0,  1, -1, 1, -1, 0, 1};
	int dy[] = {-1, -1, -1,  0, 0,  1, 1, 1};
	FOR(i, 8)
		if (!fieldEmptyExcept(board, x0 + dx[i], y0 + dy[i], x1, y1))
			return 0;
	FOR(i, 8)
		if (!fieldEmptyExcept2(board, x1 + dx[i], y1 + dy[i], x0, y0, x2, y2))
			return 0;
	FOR(i, 8)
		if (!fieldEmptyExcept(board, x2 + dx[i], y2 + dy[i], x1, y1))
			return 0;
	return 1;
}

int checkShip4(Board &board, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3) {
	if (iabs(x1 - x0) + iabs(y1 - y0) != 1)
		return 0;
	if (iabs(x2 - x1) + iabs(y2 - y1) != 1)
		return 0;
	if (iabs(x3 - x2) + iabs(y3 - y2) != 1)
		return 0;
	if (x2 - x1 != x1 - x0 || y2 - y1 != y1 - y0)
		return 0;
	if (x3 - x2 != x2 - x1 || y3 - y2 != y2 - y1)
		return 0;
	int dx[] = {-1,  0,  1, -1, 1, -1, 0, 1};
	int dy[] = {-1, -1, -1,  0, 0,  1, 1, 1};
	FOR(i, 8)
		if (!fieldEmptyExcept(board, x0 + dx[i], y0 + dy[i], x1, y1))
			return 0;
	FOR(i, 8)
		if (!fieldEmptyExcept2(board, x1 + dx[i], y1 + dy[i], x0, y0, x2, y2))
			return 0;
	FOR(i, 8)
		if (!fieldEmptyExcept2(board, x2 + dx[i], y2 + dy[i], x1, y1, x3, y3))
			return 0;
	FOR(i, 8)
		if (!fieldEmptyExcept(board, x3 + dx[i], y3 + dy[i], x2, y2))
			return 0;
	return 1;
}

int checkBoard(Board &board, int (&x)[20], int (&y)[20]) {
	int cnt = 0;
	FOR(i, 20)
		cnt += board[x[i]][y[i]];
	if (cnt != 20) {
		return 0;
	}
	
	int pos = 0;
	if (!checkShip4(board, x[pos], y[pos], x[pos + 1], y[pos + 1], x[pos + 2], y[pos + 2], x[pos + 3], y[pos + 3])) {
		return 0;
	}
	pos += 4;

	FOR(i, 2) {
		if (!checkShip3(board, x[pos], y[pos], x[pos + 1], y[pos + 1], x[pos + 2], y[pos + 2])) {
			return 0;
		}
		pos += 3;
	}

	FOR(i, 3) {
		if (!checkShip2(board, x[pos], y[pos], x[pos + 1], y[pos + 1])) {
			return 0;
		}
		pos += 2;
	}
	
	FOR(i, 4) {
		if (!checkShip1(board, x[pos], y[pos])) {
			return 0;
		}
		pos += 1;
	}

	return 1;
}

int getBoard(int player, Board &board) {
	assert(player == 0 || player == 1);

	char buf[BUF_SIZE];
	getMessage(player, buf, BUF_SIZE);
	strcpy(board_string[player], buf);
	FOR(i, 8)
		FOR(j, 8)
			board[i][j] = 0;

	int x[20], y[20];
	FOR(i, 20) {
		x[i] = buf[3*i] - 'a';
		y[i] = buf[1 + 3*i] - '1';
		board[x[i]][y[i]] = 1;
	}

	if (!checkBoard(board, x, y)) {
		fprintf(stderr, "blad: nieprawidlowa plansza\n");
		return 0;
	}
	
	return 1;
}

int getMove(int player, Move &move) {
	assert(player == 0 || player == 1);

	char buf[BUF_SIZE];
	getMessage(player, buf, BUF_SIZE);
	move.x = buf[0] - 'a';
	move.y = buf[1] - '1';

	assert(move.x >= 0 && move.x <= 7);
	assert(move.y >= 0 && move.y <= 7);
	return 1;
}

int processMove(int player, Move &move) {
	assert(player == 0 || player == 1);
	assert(move.x >= 0 && move.x <= 7);
	assert(move.y >= 0 && move.y <= 7);

	Board &board = ::board[opponent(player)];
	int field = board[move.x][move.y];

	if (field == 1) {
		int count1 = 0;
		int count2 = 1;
		
		int x, y;

		board[move.x][move.y] = 2;
		x = move.x;
		y = move.y;
		while (--x >= 0) {
			if (board[x][y] == 1)
				++count1;
			else if (board[x][y] == 2)
				++count2;
			else
				break;
		}

		x = move.x;
		y = move.y;
		while (++x <= 7) {
			if (board[x][y] == 1)
				++count1;
			else if (board[x][y] == 2)
				++count2;
			else
				break;
		}

		x = move.x;
		y = move.y;
		while (--y >= 0) {
			if (board[x][y] == 1)
				++count1;
			else if (board[x][y] == 2)
				++count2;
			else
				break;
		}

		x = move.x;
		y = move.y;
		while (++y <= 7) {
			if (board[x][y] == 1)
				++count1;
			else if (board[x][y] == 2)
				++count2;
			else
				break;
		}
		
		if (count1 > 0)
			return 'T';
		
		if (--ships[opponent(player)] > 0)
			return 'Z';
		else
			return 'W';
	}
	
	return 'N';
}

int sendResult(int player, int result) {
	assert(player == 0 || player == 1);
	assert(result == 'N' || result == 'Z' || result == 'W' || result == 'T');

	char buf[BUF_SIZE];
	sprintf(buf, "(%c)", result);
	sendMessage(player, buf);
	return 1;
}

int sendMoveAndResult(int player, Move &move, int result) {
	assert(player == 0 || player == 1);
	assert(move.x >= 0 && move.x <= 7);
	assert(move.y >= 0 && move.y <= 7);
	assert(result == 'N' || result == 'Z' || result == 'W' || result == 'T');

	char buf[BUF_SIZE];
	sprintf(buf, "(%c,%c%c)", result, move.x + 'a', move.y + '1');
	sendMessage(player, buf);
	return 1;
}

int sendBoard(int player) {
	assert(player == 0 || player == 1);

	char buf[BUF_SIZE];
	sprintf(buf, "(%s)", board_string[opponent(player)]);
	sendMessage(player, buf);
	return 1;
}

int next_round(int start_player, int round) {
	assert(start_player == 0 || start_player == 1);
	assert(round >= 1);

	sendStart(0, start_player, round);
	getBoard(0, board[0]);
	sendStart(1, start_player, round);
	getBoard(1, board[1]);
	
	ships[0] = 10;
	ships[1] = 10;
		
	int player = start_player;
	while (1) {
		Move move;

		getMove(player, move);

		int result = processMove(player, move);
		printf("%d %c\n", player, result);
		assert(move.x >= 0 && move.x <= 7);
		assert(move.y >= 0 && move.y <= 7);

		sendResult(player, result);
		sendMoveAndResult(opponent(player), move, result);
		
		if (result == 'N')
			player = opponent(player);
		else if (result == 'W')
			break;
	}
	
	sendBoard(0);
	sendBoard(1);
	
	return player == 0 ? 1 : 0;
}

int runProgram(char *filepath, FILE *&out, FILE *&in) {
	int p1[2];
	int p2[2];	
	if (pipe(p1) == -1)
		syserr("pipe");
	if (pipe(p2) == -1)
		syserr("pipe");
	
	pid_t pid;
	switch ((pid = fork())) {
		case 0: // child
			if (close(0) == -1)
				syserr("close");
			if (dup(p2[0]) == -1)
				syserr("dup");
			if (close(1) == -1)
				syserr("close");
			if (close(2) == -1)
				syserr("close");
			if (dup(p1[1]) == -1)
				syserr("dup");

			if (close(p1[0]) == -1)
				syserr("close");
			if (close(p1[1]) == -1)
				syserr("close");
			if (close(p2[0]) == -1)
				syserr("close");
			if (close(p2[1]) == -1)
				syserr("close");
			
			if (execlp(filepath, filepath, S_ROUNDS, S_TIME, NULL) == -1)
				syserr("exec");
			break;

		case -1:
			syserr("fork");
			break;

		default: // parent
			if (close(p1[1]) == -1)
				syserr("close");
			if (close(p2[0]) == -1)
				syserr("close");
			
			out = fdopen(p1[0], "r");
			in = fdopen(p2[1], "w");
			
			if (!in)
				error("fdopen");
			if (!out)
				error("fdopen");
			
			setbuf(in, 0);
			setbuf(out, 0);
			
			break;
	}
	return 1;
}

int main(int argc, char *argv[]) {
	if (argc != 3)
		error("Blad: zla liczba argumentow.\nWywolanie: arbiter program1 program2");
	
	runProgram(argv[1], output[0], input[0]);
	runProgram(argv[2], output[1], input[1]);
	
	int wins = 0;
	FOR(i, ROUNDS)
		wins += next_round(i & 1, i + 1);
	
	printf("Wynik: %d - %d\n", wins, ROUNDS - wins);
}
