]> git.cworth.org Git - grrobot/blobdiff - src/grrobot.c
Add --watch option
[grrobot] / src / grrobot.c
index d9bf0484e26ae26adbea5424b2ac67b01661f28e..209c599d9fc1db6a3f5a31fd10ca996d985cbff1 100644 (file)
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
+
 #include <gtk/gtk.h>
 
 #include "grr_board_view.h"
+#include "grr_util.h"
 #include "args.h"
 
 typedef struct grr_game {
@@ -41,12 +44,24 @@ typedef struct grr_game {
 
     GtkWidget *window;
     GtkWidget *board_view;
+    GtkWidget *command_entry;
+
     GtkTextBuffer *message_buffer;
     GtkWidget *message_view;
-    GtkWidget *command_entry;
-    GtkTextIter message_iter;
+    int msg_id;
+    int last_move_msg_id;
+    rr_robot_t last_move_robot;    
 } grr_game_t;
 
+static int
+grr_game_printf (grr_game_t *game, const char *fmt, ...);
+
+static int
+grr_game_vprintf (grr_game_t *game, const char *fmt, va_list ap);
+
+static int
+grr_game_print (grr_game_t *game, const char *msg);
+
 static int
 grr_game_start_gui (grr_game_t *game);
 
@@ -77,12 +92,22 @@ main (int argc, char **argv)
        return 1;
     }
 
-    status = rr_client_join (game.client, args.game);
-    if (status == RR_STATUS_NO_GAME) {
-       status = rr_client_new (game.client, args.game);
+    if (args.watch) {
+       status = rr_client_watch (game.client, args.game);
+       if (status == RR_STATUS_NO_GAME) {
+           fprintf (stderr, "No game %s to watch\n", args.game);
+       }
+    } else {
+       status = rr_client_join (game.client, args.game);
+       if (status == RR_STATUS_NO_GAME) {
+           status = rr_client_new (game.client, args.game);
+       }
     }
 
     game.board = rr_board_create (16, 16);
+    game.msg_id = 0;
+    game.last_move_msg_id = 0;
+    game.last_move_robot = RR_ROBOT_NONE;
 
     source = grr_game_notices_source_new (&game);
     g_source_set_priority (source, GDK_PRIORITY_EVENTS);
@@ -165,71 +190,134 @@ grr_game_read_notices (grr_game_t *game)
 {
     rr_board_t *board = game->board;
     rr_status_t status;
-    char **notice_s;
     rr_notice_t *notice;
-    int i;
 
     while (rr_client_notice_pending (game->client)) {
-       status = rr_client_next_notice (game->client, &notice_s);
+       status = rr_client_next_notice (game->client, &notice);
        if (status) {
-           fprintf (stderr, "Error during rr_client_notice: %s\n",
-                    rr_status_str (status));
+           if (status == RR_STATUS_EOF)
+               fprintf (stderr, "The server has disconnected, exiting.\n");
+           else
+               fprintf (stderr, "Error during rr_client_next_notice: %s\n",
+                        rr_status_str (status));
            gtk_exit (1);
-           return;
-       }
-       for (i=0; notice_s[i]; i++) {
-           gtk_text_buffer_insert_at_cursor (game->message_buffer, notice_s[i], -1);
-           gtk_text_buffer_insert_at_cursor (game->message_buffer, " ", -1);
        }
-       gtk_text_buffer_insert_at_cursor (game->message_buffer, "\n", -1);
-       gtk_text_buffer_get_iter_at_offset (game->message_buffer,
-                                           &game->message_iter,
-                                           -1);
-       gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (game->message_view),
-                                     &game->message_iter,
-                                     0.0, FALSE, 0.0, 0.0);
-
-       notice = rr_client_parse_notice (game->client, notice_s);
-       if (notice == NULL) {
-           int i;
-           fprintf (stderr, "Failed to parse notice: ");
-           for (i=0; notice_s[i]; i++)
-               fprintf (stderr, " %s", notice_s[i]);
-           fprintf (stderr, "\n");
-           return;
+       if (!notice) {
+           fprintf (stderr, "Missing notice\n");
+           continue;
        }
 
        switch (notice->type) {
        case RR_NOTICE_USER:
+           grr_game_printf (game,
+                            "\nGLOBAL: %s has connected.", notice->u.string);
+           break;
        case RR_NOTICE_QUIT:
+           grr_game_printf (game,
+                            "\nGLOBAL: %s has disconnected.", notice->u.string);
+           break;
        case RR_NOTICE_GAME:
+           grr_game_printf (game,
+                            "\nGLOBAL: New game available: %s.", notice->u.string);
+           break;
        case RR_NOTICE_DISPOSE:
+           grr_game_printf (game,
+                            "\nGLOBAL: Game %s has been disposed.", notice->u.string);
+           break;
        case RR_NOTICE_MESSAGE:
+           grr_game_printf (game, "\n%s> %s",
+                            notice->u.message.username,
+                            notice->u.message.text);
+           break;
        case RR_NOTICE_GAMESTATE:
+           grr_game_printf (game, "\nGame state changed to: %s.",
+                            rr_gamestate_str (notice->u.gamestate));
+           break;
+       case RR_NOTICE_TURN:
+           grr_game_print (game, "\nNew round!");
+           rr_board_set_goal_target (board, notice->u.target);
+           gtk_widget_queue_draw (GTK_WIDGET (game->window));
+           break;
+       case RR_NOTICE_GAMEOVER:
+       {
+           char *diagram;
+           grr_game_printf (game, "\nGame over. New game will begin now.");
+           /* XXX: Can drop this when the BOARD NOTICE is added in the server */
+           rr_client_show (game->client, &diagram);
+           rr_board_parse (board, diagram);
+           free (diagram);
+           gtk_widget_queue_draw (GTK_WIDGET (game->window));
+       }
+       break;
        case RR_NOTICE_JOIN:
+           grr_game_printf (game, "\nUser %s has joined the game.",
+                            notice->u.string);
+           break;
        case RR_NOTICE_WATCH:
+           grr_game_printf (game, "\nUser %s has started watching the game.",
+                            notice->u.string);
+           break;
        case RR_NOTICE_PART:
+           grr_game_printf (game, "\nUser %s has left the game.",
+                            notice->u.string);
+           break;
        case RR_NOTICE_BID:
+           grr_game_printf (game, "\nUser %s bids %d.",
+                            notice->u.bid.username,
+                            notice->u.bid.number);
+           break;
        case RR_NOTICE_REVOKE:
-       case RR_NOTICE_TIMER:
+           grr_game_printf (game, "\nUser %s revokes previous bid.",
+                            notice->u.string);
+           break;
        case RR_NOTICE_ABANDON:
+           grr_game_printf (game, "\nUser %s is willing to abandon this turn.",
+                            notice->u.string);
+           break;
        case RR_NOTICE_NOBID:
-       case RR_NOTICE_ACTIVE:
+           grr_game_printf (game, "\nUser %s sees no point in bidding further this round",
+                            notice->u.string);
+           break;
        case RR_NOTICE_MOVE:
+           if (game->msg_id == game->last_move_msg_id
+               && game->last_move_robot == notice->u.move.robot) {
+               game->last_move_msg_id = grr_game_printf (game, " %s",
+                                rr_direction_str (notice->u.move.direction));
+           } else {
+               game->last_move_msg_id = grr_game_printf (game, "\nMove #%d: %s %s",
+                                notice->u.move.count,
+                                rr_robot_str (notice->u.move.robot),
+                                rr_direction_str (notice->u.move.direction));
+               game->last_move_robot = notice->u.move.robot;
+           }
+           break;
        case RR_NOTICE_UNDO:
+           grr_game_print (game, "\nUNDO");
+           break;
        case RR_NOTICE_RESET:
+           grr_game_print (game, "\nRESET");
+           break;
        case RR_NOTICE_SCORE:
+           grr_game_printf (game, "\nScore for %s: %d.",
+                            notice->u.bid.username,
+                            notice->u.bid.number);
+           break;
        case RR_NOTICE_ACTIVATE:
-           /* XXX: Need to actually handle many of these. */
-           fprintf (stderr, "Warning: Ignoring notice of type %d\n", notice->type);
+           grr_game_printf (game, "\nYour turn.",
+                            notice->u.number);
            break;
-       case RR_NOTICE_POSITION:
-           rr_board_position (board, notice->u.position.robot,
-                              notice->u.position.x, notice->u.position.y);
-           gtk_widget_queue_draw (GTK_WIDGET (game->window));
+       case RR_NOTICE_ACTIVE:
+           grr_game_printf (game, "\nUser %s now active to demonstrate solution in %d moves.",
+                              notice->u.bid.username,
+                            notice->u.bid.number);
            break;
-       case RR_NOTICE_TURN:
-           rr_board_set_goal_target (board, notice->u.target);
+       case RR_NOTICE_TIMER:
+           grr_game_printf (game, "\nTime remaining: %d seconds.",
+                            notice->u.number);
+           break;
+       case RR_NOTICE_POSITION:
+           rr_board_position_robot (board, notice->u.position.robot,
+                                    notice->u.position.x, notice->u.position.y);
            gtk_widget_queue_draw (GTK_WIDGET (game->window));
            break;
        }
@@ -250,20 +338,13 @@ grr_game_entry_callback (GtkWidget *widget,
 
     status = rr_client_request (game->client, entry_text, &response);
     if (status) {
-       gtk_text_buffer_insert_at_cursor (game->message_buffer,
-                                         "ERROR: ", -1);
-       gtk_text_buffer_insert_at_cursor (game->message_buffer,
-                                         rr_status_str (status), -1);
-       gtk_text_buffer_insert_at_cursor (game->message_buffer,
-                                         "\n", -1);
+       grr_game_printf (game, "\nERROR: %s.", rr_status_str (status));
     } else {
-       for (i=0; response[i]; i++) {
-           gtk_text_buffer_insert_at_cursor (game->message_buffer,
-                                             response[i], -1);
-           gtk_text_buffer_insert_at_cursor (game->message_buffer,
-                                             " ", -1);
+       if (response[0]) {
+           grr_game_print (game, "\n");
+           for (i=0; response[i]; i++)
+               grr_game_printf (game, "%s ", response[i]);
        }
-       gtk_text_buffer_insert_at_cursor (game->message_buffer, "\n", -1);
     }
 
     gtk_entry_set_text (GTK_ENTRY (game->command_entry), "");
@@ -274,6 +355,60 @@ grr_game_entry_callback (GtkWidget *widget,
 
 }
 
+static int
+grr_game_printf (grr_game_t *game, const char *fmt, ...)
+{
+    int msg_id;
+
+    va_list ap;
+
+    va_start (ap, fmt);
+    msg_id = grr_game_vprintf (game, fmt, ap);
+    va_end (ap);
+
+    return msg_id;
+}
+
+static int
+grr_game_vprintf (grr_game_t *game, const char *fmt, va_list ap)
+{
+    char *msg;
+    int msg_id;
+
+    grr_sprintf_alloc_va (&msg, fmt, ap);
+    if (msg == NULL)
+       return 0;
+
+    msg_id = grr_game_print (game, msg);
+
+    free (msg);
+
+    return msg_id;
+}
+
+static int
+grr_game_print (grr_game_t *game, const char *msg)
+{
+    GtkTextIter iter;
+    GtkTextMark *mark;
+
+    /* There might be a lighter way to do this, but this seems to
+       work. */
+
+    gtk_text_buffer_get_end_iter (game->message_buffer,        &iter);
+
+    mark = gtk_text_buffer_get_mark (game->message_buffer, "end");
+
+    gtk_text_buffer_move_mark (game->message_buffer, mark, &iter);
+
+    gtk_text_buffer_insert (game->message_buffer, &iter, msg, -1);
+
+    gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (game->message_view),
+                                 mark, 0.0, TRUE, 0.0, 1.0);
+
+    return ++game->msg_id;
+}
+
 static int
 grr_game_start_gui (grr_game_t *game)
 {
@@ -298,9 +433,14 @@ grr_game_start_gui (grr_game_t *game)
     vbox = gtk_vbox_new (FALSE, 1);
     gtk_container_add (GTK_CONTAINER (window), vbox);
     {
+       GtkTextIter iter;
        game->board_view = grr_board_view_new (game->board);
        grr_board_view_set_client (GRR_BOARD_VIEW (game->board_view), game->client);
        game->message_buffer = gtk_text_buffer_new (NULL);
+       gtk_text_buffer_get_iter_at_offset (game->message_buffer,
+                                           &iter, -1);
+       gtk_text_buffer_create_mark (game->message_buffer,
+                                    "end", &iter, FALSE);
        game->message_view = gtk_text_view_new_with_buffer (game->message_buffer);
        game->command_entry = gtk_entry_new ();
 
@@ -308,7 +448,6 @@ grr_game_start_gui (grr_game_t *game)
        gtk_container_set_border_width (GTK_CONTAINER (vpaned), 0);
        gtk_box_pack_start (GTK_BOX (vbox), vpaned,
                            TRUE, TRUE, 0);
-       gtk_container_add (GTK_CONTAINER (vbox), vpaned);
        {
            board_frame = gtk_aspect_frame_new (NULL, 0.5, 0.5, 1.0, FALSE);
            gtk_paned_pack1 (GTK_PANED (vpaned), board_frame, TRUE, TRUE);