#include #include #include #include #include #include #include #include "TomiTCP/net.h" using namespace std; using namespace net; #define REVISION "2" const int towin = 5; enum { PLACE_OK = 0, PLACE_CANNOT, PLACE_WIN }; struct score_t { int green, red; } score = {0, 0}; class skvorky_t { public: char *pole; int player; int width, height; int winner; score_t *score; int lastx, lasty; skvorky_t() : pole(0), player(0), width(0), height(0), winner(0), score(0), lastx(0), lasty(0) { } skvorky_t(int w, int h, score_t *sc, int startplayer) : player(startplayer), width(w), height(h), winner(0), score(sc), lastx(0), lasty(0) { pole = new char[w*h]; memset(pole, 0, w*h); } ~skvorky_t() { delete pole; } char * operator [](int index) { return pole + index * width; } void print(); int place(int y, int x); }; void skvorky_t::print() { int a = 0; cout << "\033[H"; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { a = !a; switch ((*this)[i][j]) { case 1: cout << "\033[42;30;1m" << ((lastx == j && lasty == i)?"[]":"`."); break; case 2: cout << "\033[41;30;1m" << ((lastx == j && lasty == i)?"[]":"`."); break; default: cout << "\033[" << (a?"40":"49") << ";39;22m` "; } } a = !a; cout << "\033[0m"; if (i == 3) { if (winner == 3) cout << " Draw!"; else if (winner) cout << " " << (winner==1?"Green":"Red") << " won!"; else cout << " It's " << (player==1?"green":"red") << "'s turn."; } if (i == 1 && score) { cout.width(5); cout << " Green: " << score->green << " Red: " << score->red; } cout << "\033[K" << endl; } cout << "\033[J" << flush; } int skvorky_t::place(int y, int x) { if (y >= height || x >= width || y < 0 || x < 0 || (*this)[y][x] || winner) return PLACE_CANNOT; (*this)[y][x] = player; lastx = x; lasty = y; int win = 0; if (!win) { int left = 0, right = 0; for (int x_ = x - 1; x_ >= 0 && (*this)[y][x_] == player; x_--, left++); for (int x_ = x + 1; x_ < width && (*this)[y][x_] == player; x_++, right++); if (left + right >= towin - 1) win = 1; } if (!win) { int up = 0, down = 0; for (int y_ = y - 1; y_ >= 0 && (*this)[y_][x] == player; y_--, up++); for (int y_ = y + 1; y_ < height && (*this)[y_][x] == player; y_++, down++); if (up + down >= towin - 1) win = 1; } if (!win) { int upleft = 0, downright = 0; for (int y_ = y - 1, x_ = x - 1; y_ >= 0 && x_ >= 0 && (*this)[y_][x_] == player; y_--, x_--, upleft++); for (int y_ = y + 1, x_ = x + 1; y_ < height && x_ < width && (*this)[y_][x_] == player; y_++, x_++, downright++); if (upleft + downright >= towin - 1) win = 1; } if (!win) { int upright = 0, downleft = 0; for (int y_ = y - 1, x_ = x + 1; y_ >= 0 && x_ < width && (*this)[y_][x_] == player; y_--, x_++, upright++); for (int y_ = y + 1, x_ = x - 1; y_ < height && x_ >= 0 && (*this)[y_][x_] == player; y_++, x_--, downleft++); if (upright + downleft >= towin - 1) win = 1; } if (!win) { int i; for (i = 0; i < width * height && pole[i]; i++); if (i >= width * height) win = 2; } if (win) { if (win == 2) winner = 3; else winner = player; if (score) { if (winner == 1) score->green++; else if (winner == 2) score->red++; } return PLACE_WIN; } player++; if (player > 2) player = 1; return PLACE_OK; } TomiTCP *c = 0; struct termios saved_attributes; void term_end(); void sigint(int); void term_start() { struct termios tattr; if (!isatty (STDIN_FILENO)) { fprintf (stderr, "Not a terminal.\n"); exit (EXIT_FAILURE); } tcgetattr (STDIN_FILENO, &saved_attributes); tcgetattr (STDIN_FILENO, &tattr); tattr.c_lflag &= ~(ICANON|ECHO); tattr.c_cc[VMIN] = 1; tattr.c_cc[VTIME] = 0; tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr); cout << "\033[?25l\033[?1000h" << flush; signal(SIGINT, sigint); atexit(term_end); } void term_end() { tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes); cout << "\033[?25h\033[?1000l" << flush; delete c; } void sigint(int) { exit(0); } int main(int argc, char *argv[]) { srand(time(0) ^ getpid()); int width = 16, height = 16; int myplayer = 0; int startplayer = (rand() % 2) + 1; try { if (argc == 2) { myplayer = 1; TomiTCP s(atol(argv[1])); c = s.accept(); fprintf(*c, "skvorky-" REVISION "\n"); string t; if (!c->getline(t)) throw runtime_error("Remote host closed connection"); if (t != "skvorky-" REVISION) throw runtime_error("Incompatible protocol (\"skvorky-" REVISION "\" != \"" + t + "\")"); fprintf(*c, "%i\n%i\n%i\n", width, height, startplayer); } else if (argc == 3) { myplayer = 2; c = new TomiTCP; c->connect(argv[1], argv[2]); fprintf(*c, "skvorky-" REVISION "\n"); string t; if (!c->getline(t)) throw runtime_error("Remote host closed connection"); if (t != "skvorky-" REVISION) throw runtime_error("Incompatible protocol (\"skvorky-" REVISION "\" != \"" + t + "\")"); if (!c->getline(t)) throw runtime_error("Remote host closed connection"); if (!(width = atol(t.c_str()))) throw runtime_error("We didn't get valid width"); if (!c->getline(t)) throw runtime_error("Remote host closed connection"); if (!(height = atol(t.c_str()))) throw runtime_error("We didn't get valid height"); if (!c->getline(t)) throw runtime_error("Remote host closed connection"); if (!(startplayer = atol(t.c_str()))) throw runtime_error("We didn't get valid startplayer"); } } catch (runtime_error e) { cerr << e.what() << endl; delete c; return -1; } term_start(); #define INIT delete skvorky; skvorky = new skvorky_t(width, height, &score, startplayer); skvorky_t *skvorky = 0; INIT; skvorky->print(); try { while (1) { fd_set set; FD_ZERO(&set); if (c) FD_SET(c->sock, &set); FD_SET(0, &set); int ret = TEMP_FAILURE_RETRY(select(FD_SETSIZE,&set,NULL,NULL,NULL)); if (ret == -1) throw runtime_error("error in select: "+string(strerror(errno))); if (FD_ISSET(0, &set)) { char ch = cin.get(); int b, x, y; if (ch == '\033' && (ch = cin.get()) != EOF && ch == '[' && (ch = cin.get()) != EOF && ch == 'M' && (b = cin.get()) != EOF && (b -= 040) >= 0 && (x = cin.get()) != EOF && (x -= 040) >= 0 && (y = cin.get()) != EOF && (y -= 040) >= 0 && b == 0) { if (myplayer && skvorky->player != myplayer) { cout << "\a" << flush; continue; } int ret = skvorky->place(y - 1, (x - 1) / 2); if (ret == PLACE_CANNOT) { cout << "\a" << flush; continue; } if (c) fprintf(*c, "%i\n%i\n", ((x - 1) / 2) + 1, y); skvorky->print(); } else if (ch == '\014') { skvorky->print(); } if (ch == '\n' && skvorky->winner) { if (c) { cout << "\033[HWaiting for remote to accept new round.\033[K" << endl << "\033[J" << flush; fprintf(*c, "new\n"); string t; if (!c->getline(t)) throw runtime_error("Remote host closed connection"); if (t != "new") throw runtime_error("Game or protocol error."); } startplayer++; if (startplayer > 2) startplayer = 1; INIT; skvorky->print(); } } if (c && FD_ISSET(c->sock, &set)) { string t; int x,y; if (!c->getline(t)) throw runtime_error("Remote host closed connection"); if (t == "new") { if (skvorky->winner) { cout << "\033[HPress any key to accept new round.\033[K" << endl << "\033[J" << flush; cin.ignore(); fprintf(*c, "new\n"); startplayer++; if (startplayer > 2) startplayer = 1; INIT; skvorky->print(); } else { fprintf(*c, "no\n"); } } else { if (!(x = atol(t.c_str()))) throw runtime_error("Protocol error."); if (!c->getline(t)) throw runtime_error("Remote host closed connection"); if (!(y = atol(t.c_str()))) throw runtime_error("Protocol error."); if (myplayer && skvorky->player == myplayer) { throw runtime_error("Game or protocol error."); } if (skvorky->place(y - 1, x - 1) == PLACE_CANNOT) { throw runtime_error("Game error."); } skvorky->print(); } } } } catch (runtime_error e) { cerr << e.what() << endl; return -1; } return 0; }