Index: plugins/roard/Makefile
===================================================================
--- plugins/roard/Makefile	(revision 5426)
+++ plugins/roard/Makefile	(revision 5449)
@@ -2,5 +2,5 @@
 include ../../Makefile.inc
 
-TARGETS=echo$(SHARED_SUFFIX) daytime$(SHARED_SUFFIX) discard$(SHARED_SUFFIX) irc$(SHARED_SUFFIX) listenpty$(SHARED_SUFFIX)
+TARGETS=echo$(SHARED_SUFFIX) daytime$(SHARED_SUFFIX) discard$(SHARED_SUFFIX) irc$(SHARED_SUFFIX) tic-tac-toe$(SHARED_SUFFIX) listenpty$(SHARED_SUFFIX)
 
 #DEFINES        = -DDEBUG
Index: plugins/universal/tic-tac-toe.c
===================================================================
--- plugins/universal/tic-tac-toe.c	(revision 5449)
+++ plugins/universal/tic-tac-toe.c	(revision 5449)
@@ -0,0 +1,323 @@
+//tic-tac-toe.c:
+
+/*
+ *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2012
+ *
+ *  This file is part of roard a part of RoarAudio,
+ *  a cross-platform sound system for both, home and professional use.
+ *  See README for details.
+ *
+ *  This file is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 3
+ *  as published by the Free Software Foundation.
+ *
+ *  RoarAudio is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this software; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ *  Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <roard/include/roard.h>
+
+#define _FREE     ' '
+#define _PLAYER   'X'
+#define _COMPUTER 'O'
+#define _NOBODY   '#'
+
+#define _DISPLAY \
+"\e[2J\e[H" \
+"Game board:     IDs:\n"        \
+" %c | %c | %c      0 | 1 | 2\n"   \
+"---+---+---     ---+---+---\n" \
+" %c | %c | %c      3 | 4 | 5\n"   \
+"---+---+---     ---+---+---\n" \
+" %c | %c | %c      6 | 7 | 8\n"   \
+"\n" \
+"%s\n"
+#define _DISPLAY_BUFFER_LEN 1024
+
+#define MSG__YOUR_TURN       "It's your turn. -- press any ID or q to quit --"
+#define MSG__CANNOT_PUT_HERE "You can not put your mark here. -- try again another position --"
+#define MSG__WON_PLAYER      "You won. -- press any key to continue or q to quit --"
+#define MSG__WON_COMPUTER    "I won. -- press any key to continue or q to quit --"
+#define MSG__WON_NOBODY      "Nobody won. -- press any key to continue or q to quit --"
+
+typedef char game_state[9];
+
+struct test_result {
+ int score_player, score_computer;
+ int free;
+};
+
+struct stats {
+ int free;
+ struct test_result results[9];
+};
+
+static void new_game(game_state * state) {
+ memset(state, _FREE, sizeof(game_state));
+}
+
+static int try_put(game_state * state, int id, char player) {
+ if ( id < 0 || id > 8 )
+  return -1;
+
+ if ( (*state)[id] == _FREE ) {
+  (*state)[id] = player;
+  return 0;
+ } else {
+  return -1;
+ }
+};
+
+static void analyze(struct stats * stats, game_state * state) {
+ static const int textvec[3][3][2] = {
+  {{0, 3}, {1, 3}, {2,  3}},
+  {{0, 1}, {3, 1}, {6,  1}},
+  {{0, 1}, {4, 0}, {8, -1}}
+ };
+ struct test_result * res;
+ int i, j;
+ int test;
+
+ stats->free = 0;
+
+ for (i = 0; i < 9; i++)
+  if ( (*state)[i] == _FREE )
+   stats->free++;
+
+ for (test = 0; test < 3; test++) {
+  for (i = 0; i < 3; i++) {
+   res = &(stats->results[i+test*3]);
+   res->score_player = 0;
+   res->score_computer = 0;
+   res->free = -1;
+   for (j = 0; j < 3; j++) {
+    switch ((*state)[textvec[test][j][0] + textvec[test][j][1]*i]) {
+     case _PLAYER: res->score_player++; break;
+     case _COMPUTER: res->score_computer++; break;
+     case _FREE: res->free = textvec[test][j][0] + textvec[test][j][1]*i; break;
+    }
+   }
+  }
+ }
+}
+
+static char check_won(game_state * state) {
+ struct stats stats;
+ int i;
+
+ analyze(&stats, state);
+
+ for (i = 0; i < 9; i++) {
+  if ( stats.results[i].score_player == 3 )
+   return _PLAYER;
+  if ( stats.results[i].score_computer == 3 )
+   return _COMPUTER;
+ }
+
+ if ( stats.free == 0)
+  return _NOBODY;
+
+ return 0;
+}
+
+static void auto_move(game_state * state, int player_move) {
+ static const int corner[4] = {0, 2, 6, 8};
+ static const int side[4]   = {1, 3, 5, 7};
+ struct stats stats;
+ struct test_result * res;
+ int i;
+
+ analyze(&stats, state);
+
+ // step 0: try to win.
+ for (i = 0; i < 9; i++) {
+  res = &(stats.results[i]);
+  if ( res->score_computer == 2 && res->free != -1 ) {
+   if ( try_put(state, res->free, _COMPUTER) == 0 )
+    return;
+  }
+ }
+
+ // step 1: block winning
+ for (i = 0; i < 9; i++) {
+  res = &(stats.results[i]);
+  if ( res->score_player == 2 && res->free != -1 ) {
+   if ( try_put(state, res->free, _COMPUTER) == 0 )
+    return;
+  }
+ }
+
+ // step 2: fork
+ // TODO: implement
+
+ // step 3: block forking
+ // TODO: implement
+
+ // step 4: play center
+ if ( try_put(state, 4, _COMPUTER) == 0 )
+  return;
+
+ // step 5: "Opposite corner"
+ for (i = 0; i < 4; i++)
+  if ( player_move == corner[i] )
+   if ( try_put(state, 8 - player_move, _COMPUTER) == 0 )
+    return;
+
+ // step 6: Empty corner
+ for (i = 0; i < 4; i++)
+  if ( try_put(state, corner[i], _COMPUTER) == 0 )
+   return;
+
+ // step 7: Empty side
+ for (i = 0; i < 4; i++)
+  if ( try_put(state, side[i], _COMPUTER) == 0 )
+   return;
+}
+
+static void draw_game(int client, game_state * state, const char * info) {
+ struct roar_buffer * buf;
+ ssize_t len = 0;
+ void * data;
+
+ if ( roar_buffer_new_data(&buf, _DISPLAY_BUFFER_LEN, &data) == -1 )
+  return;
+
+ snprintf(data, _DISPLAY_BUFFER_LEN, _DISPLAY,
+          (*state)[0], (*state)[1], (*state)[2],
+          (*state)[3], (*state)[4], (*state)[5],
+          (*state)[6], (*state)[7], (*state)[8],
+          info);
+
+ len = roar_mm_strlen(data);
+
+ if ( roar_buffer_set_len(buf, len) == -1 ) {
+  roar_buffer_free(buf);
+  clients_delete(client);
+ }
+
+ clients_add_output(client, &buf);
+}
+
+int new_client(int client, struct roar_vio_calls * vio, struct roard_listen * lsock) {
+ struct roar_client_server * cs;
+ game_state * state;
+
+ (void)vio, (void)lsock;
+
+ if ( clients_get_server(client, &cs) == -1 )
+  return -1;
+
+ state = roar_mm_malloc(sizeof(game_state));
+ if ( state == NULL )
+  return -1;
+
+ new_game(state);
+
+ cs->protoinst = state;
+
+ draw_game(client, state, MSG__YOUR_TURN);
+ return 0;
+}
+
+static int check_client(int client, struct roar_vio_calls * vio) {
+ struct roar_client_server * cs;
+ char buf[32];
+ ssize_t len;
+ ssize_t i;
+ const char * msg = MSG__YOUR_TURN;
+ char won;
+ int player_move = -1;
+
+ ROAR_DBG("check_client(client=%i, vio=%p) = ?", client, vio);
+
+ if ( clients_get_server(client, &cs) == -1 )
+  return -1;
+
+ len = roar_vio_read(vio, buf, sizeof(buf));
+
+ if ( len < 1 ) {
+  clients_delete(client);
+  return -1;
+ }
+
+ if ( check_won(cs->protoinst) ) {
+  for (i = 0; i < len; i++) {
+   if ( buf[i] == 'q' ) {
+    clients_delete(client);
+    return -1;
+   } else {
+    new_game(cs->protoinst);
+    draw_game(client, cs->protoinst, MSG__YOUR_TURN);
+    return 0;
+   }
+  }
+ }
+
+ for (i = 0; i < len; i++) {
+  switch (buf[i]) {
+   case 'q':
+     clients_delete(client);
+     return -1;
+    break;
+   case '0': case '1': case '2': case '3': case '4':
+   case '5': case '6': case '7': case '8':
+     if ( try_put(cs->protoinst, buf[i] - '0', _PLAYER) == -1 ) {
+      msg = MSG__CANNOT_PUT_HERE;
+      draw_game(client, cs->protoinst, msg);
+      return 0;
+     } else {
+      msg = MSG__YOUR_TURN;
+      i = len; // end the loop.
+      player_move = buf[i] - '0';
+     }
+    break;
+  }
+ }
+
+ won = check_won(cs->protoinst);
+
+ if ( !won && player_move != -1 ) {
+  auto_move(cs->protoinst, player_move);
+  won = check_won(cs->protoinst);
+ }
+
+ switch (won) {
+  case _PLAYER: msg = MSG__WON_PLAYER; break;
+  case _COMPUTER: msg = MSG__WON_COMPUTER; break;
+  case _NOBODY: msg = MSG__WON_NOBODY; break;
+ }
+
+ draw_game(client, cs->protoinst, msg);
+
+ ROAR_DBG("check_client(client=%i, vio=%p) = 0", client, vio);
+ return 0;
+}
+
+
+static struct roard_proto proto[1] = {
+ {ROAR_PROTO_GAME, ROAR_SUBSYS_NONE, "Simple Tic-Tac-Toe game", NULL, new_client, NULL, check_client, NULL, NULL}
+};
+
+ROARD_DL_REG_PROTO(proto)
+
+ROAR_DL_PLUGIN_START(protocol_tic_tac_toe) {
+ ROARD_DL_CHECK_VERSIONS();
+
+ ROAR_DL_PLUGIN_META_PRODUCT_NIV("protocol-tic-tac-toe", ROAR_VID_ROARAUDIO, ROAR_VNAME_ROARAUDIO);
+ ROAR_DL_PLUGIN_META_VERSION(ROAR_VERSION_STRING);
+ ROAR_DL_PLUGIN_META_LICENSE_TAG(GPLv3_0);
+ ROAR_DL_PLUGIN_META_CONTACT_FLNE("Philipp", "Schafft", "ph3-der-loewe", "lion@lion.leolix.org");
+ ROAR_DL_PLUGIN_META_DESC("Implementation of a the well known game Tic-Tac-Toe");
+
+ ROARD_DL_REGFN_PROTO();
+} ROAR_DL_PLUGIN_END
+
+//ll
