source: roaraudio/plugins/universal/tic-tac-toe.c @ 6065:4b251ed10391

Last change on this file since 6065:4b251ed10391 was 6065:4b251ed10391, checked in by phi, 9 years ago

Fixed tic-tac-toe plugin, print actual host, not static ROAR_VERSION_STRING

File size: 11.1 KB
Line 
1//tic-tac-toe.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2012-2014
5 *
6 *  This file is part of roard a part of RoarAudio,
7 *  a cross-platform sound system for both, home and professional use.
8 *  See README for details.
9 *
10 *  This file is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 3
12 *  as published by the Free Software Foundation.
13 *
14 *  RoarAudio is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this software; see the file COPYING.  If not, write to
21 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
22 *  Boston, MA 02110-1301, USA.
23 *
24 */
25
26#include <roaraudio.h>
27
28#define _FREE     ' '
29#define _PLAYER   'X'
30#define _COMPUTER 'O'
31#define _NOBODY   '#'
32
33#define _DISPLAY \
34/* Bring Telnet in right mode: */ \
35/* IAC WILL ECHO IAC WILL SUPPRESS_GO_AHEAD IAC WONT LINEMODE */ \
36/* 255  251    1 255  251                 3 255  252       34 */ \
37"\377\373\001\377\373\003\377\374\042" \
38"\e[2J\e[H" \
39" Tic Tac Toe\r\n" \
40" (running: %s, API: %s)\r\n" \
41"\r\n" \
42"Game board:     IDs:\r\n"        \
43" %c | %c | %c      7 | 8 | 9\r\n"   \
44"---+---+---     ---+---+---\r\n" \
45" %c | %c | %c      4 | 5 | 6\r\n"   \
46"---+---+---     ---+---+---\r\n" \
47" %c | %c | %c      1 | 2 | 3\r\n"   \
48"\r\n" \
49"%s\r\n"
50#define _DISPLAY_BUFFER_LEN 1024
51
52#define _HELP \
53"\e[2J\e[H" \
54" Tic Tac Toe\r\n" \
55" (running: %s, API: %s)\r\n" \
56"\r\n" \
57"IDs:            Alternative IDs:\r\n" \
58" 7 | 8 | 9       a | s | d\r\n"   \
59"---+---+---     ---+---+---\r\n"  \
60" 4 | 5 | 6       f |SPC| h\r\n"   \
61"---+---+---     ---+---+---\r\n"  \
62" 1 | 2 | 3       j | k | l\r\n"   \
63"\r\n" \
64"Other keys:\r\n" \
65" q g , .  - Quit\r\n" \
66" n +      - New game\r\n" \
67" H 0      - Help\r\n" \
68" e        - Exit help / Redraw game\r\n" \
69"\r\n" \
70"%s\r\n"
71
72#define MSG__YOUR_TURN       "It's your turn. -- press any ID or H for help, q to quit --"
73#define MSG__CANNOT_PUT_HERE "You can not put your mark here. -- try again another position --"
74#define MSG__WON_PLAYER      "You won. -- press any key to continue or H for help, q to quit --"
75#define MSG__WON_COMPUTER    "I won. -- press any key to continue or H for help, q to quit --"
76#define MSG__WON_NOBODY      "Nobody won. -- press any key to continue or H for help, q to quit --"
77#define MSG__HELP            "-- press e to continue or q to quit --"
78
79typedef char game_state[9];
80
81struct test_result {
82 int score_player, score_computer;
83 int free;
84};
85
86struct stats {
87 int free;
88 struct test_result results[9];
89};
90
91static void new_game(game_state * state) {
92 memset(state, _FREE, sizeof(game_state));
93}
94
95static int try_put(game_state * state, int id, char player) {
96 if ( id < 0 || id > 8 )
97  return -1;
98
99 if ( (*state)[id] == _FREE ) {
100  (*state)[id] = player;
101  return 0;
102 } else {
103  return -1;
104 }
105};
106
107static void analyze(struct stats * stats, game_state * state) {
108 static const int textvec[3][3][2] = {
109  {{0, 3}, {1, 3}, {2,  3}},
110  {{0, 1}, {3, 1}, {6,  1}},
111  {{0, 1}, {4, 0}, {8, -1}}
112 };
113 struct test_result * res;
114 int i, j;
115 int test;
116
117 stats->free = 0;
118
119 for (i = 0; i < 9; i++)
120  if ( (*state)[i] == _FREE )
121   stats->free++;
122
123 for (test = 0; test < 3; test++) {
124  for (i = 0; i < 3; i++) {
125   res = &(stats->results[i+test*3]);
126   res->score_player = 0;
127   res->score_computer = 0;
128   res->free = -1;
129   for (j = 0; j < 3; j++) {
130    switch ((*state)[textvec[test][j][0] + textvec[test][j][1]*i]) {
131     case _PLAYER: res->score_player++; break;
132     case _COMPUTER: res->score_computer++; break;
133     case _FREE: res->free = textvec[test][j][0] + textvec[test][j][1]*i; break;
134    }
135   }
136  }
137 }
138}
139
140static char check_won(game_state * state) {
141 struct stats stats;
142 int i;
143
144 analyze(&stats, state);
145
146 for (i = 0; i < 9; i++) {
147  if ( stats.results[i].score_player == 3 )
148   return _PLAYER;
149  if ( stats.results[i].score_computer == 3 )
150   return _COMPUTER;
151 }
152
153 if ( stats.free == 0)
154  return _NOBODY;
155
156 return 0;
157}
158
159static void auto_move(game_state * state, int player_move) {
160 static const int corner[4] = {0, 2, 6, 8};
161 static const int side[4]   = {1, 3, 5, 7};
162 struct stats stats;
163 struct test_result * res;
164 int i;
165
166 analyze(&stats, state);
167
168 // step 0: try to win.
169 for (i = 0; i < 9; i++) {
170  res = &(stats.results[i]);
171  if ( res->score_computer == 2 && res->free != -1 ) {
172   if ( try_put(state, res->free, _COMPUTER) == 0 )
173    return;
174  }
175 }
176
177 // step 1: block winning
178 for (i = 0; i < 9; i++) {
179  res = &(stats.results[i]);
180  if ( res->score_player == 2 && res->free != -1 ) {
181   if ( try_put(state, res->free, _COMPUTER) == 0 )
182    return;
183  }
184 }
185
186 // step 2: fork
187 // TODO: implement
188
189 // step 3: block forking
190 // TODO: implement
191
192 // step 4: play center
193 if ( try_put(state, 4, _COMPUTER) == 0 )
194  return;
195
196 // step 5: "Opposite corner"
197 for (i = 0; i < 4; i++)
198  if ( player_move == corner[i] )
199   if ( try_put(state, 8 - player_move, _COMPUTER) == 0 )
200    return;
201
202 // step 6: Empty corner
203 for (i = 0; i < 4; i++)
204  if ( try_put(state, corner[i], _COMPUTER) == 0 )
205   return;
206
207 // step 7: Empty side
208 for (i = 0; i < 4; i++)
209  if ( try_put(state, side[i], _COMPUTER) == 0 )
210   return;
211}
212
213static void draw_help(struct roar_buffer ** obuffer, struct roar_dl_librarypara * pluginpara) {
214 struct roar_buffer * buf;
215 ssize_t len = 0;
216 void * data;
217
218 if ( roar_buffer_new_data(&buf, _DISPLAY_BUFFER_LEN, &data) == -1 )
219  return;
220
221 snprintf(data, _DISPLAY_BUFFER_LEN, _HELP,
222          pluginpara == NULL || pluginpara->appname    == NULL ? "<unknown>" : pluginpara->appname,
223          pluginpara == NULL || pluginpara->abiversion == NULL ? "<unknown>" : pluginpara->abiversion,
224          MSG__HELP);
225
226 len = roar_mm_strlen(data);
227
228 if ( roar_buffer_set_len(buf, len) == -1 ) {
229  roar_buffer_free(buf);
230 }
231
232 _LIBROAR_IGNORE_RET(roar_buffer_moveintoqueue(obuffer, &buf));
233}
234
235static void draw_game(game_state * state, const char * info, struct roar_buffer ** obuffer, struct roar_dl_librarypara * pluginpara) {
236 struct roar_buffer * buf;
237 ssize_t len = 0;
238 void * data;
239
240 if ( roar_buffer_new_data(&buf, _DISPLAY_BUFFER_LEN, &data) == -1 )
241  return;
242
243 snprintf(data, _DISPLAY_BUFFER_LEN, _DISPLAY,
244          pluginpara == NULL || pluginpara->appname    == NULL ? "<unknown>" : pluginpara->appname,
245          pluginpara == NULL || pluginpara->abiversion == NULL ? "<unknown>" : pluginpara->abiversion,
246          (*state)[0], (*state)[1], (*state)[2],
247          (*state)[3], (*state)[4], (*state)[5],
248          (*state)[6], (*state)[7], (*state)[8],
249          info);
250
251 len = roar_mm_strlen(data);
252
253 if ( roar_buffer_set_len(buf, len) == -1 ) {
254  roar_buffer_free(buf);
255 }
256
257 _LIBROAR_IGNORE_RET(roar_buffer_moveintoqueue(obuffer, &buf));
258}
259
260static int _set_proto(int client, struct roar_vio_calls * vio, struct roar_buffer ** obuffer, void ** userdata, const struct roar_keyval * protopara, ssize_t protoparalen, struct roar_dl_librarypara * pluginpara) {
261 game_state * state;
262
263 (void)client, (void)vio, (void)obuffer, (void)protopara, (void)protoparalen;
264
265 ROAR_DBG("_set_proto(*) = ?");
266
267 state = roar_mm_malloc(sizeof(game_state));
268 if ( state == NULL )
269  return -1;
270
271 new_game(state);
272
273 *userdata = state;
274
275 draw_game(state, MSG__YOUR_TURN, obuffer, pluginpara);
276
277 ROAR_DBG("_set_proto(client=%i, vio=%p, obuffer=%p, userdata=%p{%p}, protopara=%p, protoparalen=?, pluginpara=%p) = 0", client, vio, obuffer, userdata, *userdata, protopara, pluginpara);
278 return 0;
279}
280
281static char map_input(char c) {
282 switch (c) {
283  case ',':
284  case '.':
285  case 'g':
286    return 'q';
287   break;
288  case 'a': return '0'; break;
289  case 's': return '1'; break;
290  case 'd': return '2'; break;
291  case 'f': return '3'; break;
292  case ' ': return '4'; break;
293  case 'h': return '5'; break;
294  case 'j': return '6'; break;
295  case 'k': return '7'; break;
296  case 'l': return '8'; break;
297  case '+': return 'n'; break;
298  case '0': return 'H'; break;
299 }
300
301 if ( c > '9' )
302  return c;
303 if ( c >= '7' )
304  return c - 7;
305 if ( c >= '4' )
306  return c - 1;
307 if ( c >= '1' )
308  return c + 5;
309 return c;
310}
311
312static int _handle(int client, struct roar_vio_calls * vio, struct roar_buffer ** obuffer, void ** userdata, const struct roar_keyval * protopara, ssize_t protoparalen, struct roar_dl_librarypara * pluginpara) {
313 char buf[32];
314 ssize_t len;
315 ssize_t i;
316 const char * msg = MSG__YOUR_TURN;
317 char won;
318 int player_move = -1;
319 char c;
320
321 (void)client, (void)obuffer, (void)protopara, (void)protoparalen;
322
323 ROAR_DBG("_handle(client=%i, vio=%p, obuffer=%p, userdata=%p{%p}, protopara=%p, protoparalen=?, pluginpara=%p) = ?", client, vio, obuffer, userdata, *userdata, protopara, pluginpara);
324
325 len = roar_vio_read(vio, buf, sizeof(buf));
326
327 if ( len < 1 ) {
328  return -1;
329 }
330
331 if ( check_won(*userdata) ) {
332  for (i = 0; i < len; i++) {
333   if ( map_input(buf[i]) == 'q' ) {
334    return -1;
335   } else {
336    new_game(*userdata);
337    draw_game(*userdata, MSG__YOUR_TURN, obuffer, pluginpara);
338    return 0;
339   }
340  }
341 }
342
343 for (i = 0; i < len; i++) {
344  c = map_input(buf[i]);
345  switch (c) {
346   case 'q':
347     return -1;
348    break;
349   case 'n':
350     new_game(*userdata);
351     draw_game(*userdata, MSG__YOUR_TURN, obuffer, pluginpara);
352     return 0;
353    break;
354   case 'H':
355     draw_help(obuffer, pluginpara);
356     return 0;
357    break;
358   case 'e':
359     draw_game(*userdata, MSG__YOUR_TURN, obuffer, pluginpara);
360     return 0;
361    break;
362   case '0': case '1': case '2': case '3': case '4':
363   case '5': case '6': case '7': case '8':
364     if ( try_put(*userdata, c - '0', _PLAYER) == -1 ) {
365      msg = MSG__CANNOT_PUT_HERE;
366      draw_game(*userdata, msg, obuffer, pluginpara);
367      return 0;
368     } else {
369      msg = MSG__YOUR_TURN;
370      i = len; // end the loop.
371      player_move = c - '0';
372     }
373    break;
374  }
375 }
376
377 won = check_won(*userdata);
378
379 if ( !won && player_move != -1 ) {
380  auto_move(*userdata, player_move);
381  won = check_won(*userdata);
382 }
383
384 switch (won) {
385  case _PLAYER: msg = MSG__WON_PLAYER; break;
386  case _COMPUTER: msg = MSG__WON_COMPUTER; break;
387  case _NOBODY: msg = MSG__WON_NOBODY; break;
388 }
389
390 draw_game(*userdata, msg, obuffer, pluginpara);
391
392 ROAR_DBG("check_client(client=%i, vio=%p) = 0", client, vio);
393 return 0;
394}
395
396
397static const struct roar_dl_proto proto = {
398 .proto = ROAR_PROTO_GAME,
399 .description = "Simple Tic-Tac-Toe game",
400 .flags = ROAR_DL_PROTO_FLAGS_NONE,
401 .set_proto = _set_proto,
402 .unset_proto = NULL,
403 .handle = _handle,
404 .flush = NULL,
405 .flushed = NULL,
406 .status = NULL
407};
408
409static int __reg_proto(struct roar_dl_librarypara * para, struct roar_dl_libraryinst * lib) {
410 (void)para, (void)lib;
411 ROAR_DL_PLUGIN_REG_FN(ROAR_DL_PROTO_SUBTYPE, proto, ROAR_DL_PROTO_VERSION);
412 return 0;
413}
414
415ROAR_DL_PLUGIN_START(protocol_tic_tac_toe) {
416 ROAR_DL_PLUGIN_META_PRODUCT_NIV("protocol-tic-tac-toe", ROAR_VID_ROARAUDIO, ROAR_VNAME_ROARAUDIO);
417 ROAR_DL_PLUGIN_META_VERSION(ROAR_VERSION_STRING);
418 ROAR_DL_PLUGIN_META_LICENSE_TAG(GPLv3_0);
419 ROAR_DL_PLUGIN_META_CONTACT_FLNE("Philipp", "Schafft", "ph3-der-loewe", "lion@lion.leolix.org");
420 ROAR_DL_PLUGIN_META_DESC("Implementation of a the well known game Tic-Tac-Toe");
421
422 ROAR_DL_PLUGIN_REG(ROAR_DL_FN_PROTO, __reg_proto);
423} ROAR_DL_PLUGIN_END
424
425//ll
Note: See TracBrowser for help on using the repository browser.