main.vala 6.67 KB
Newer Older
1 2 3 4 5
/*
 * A sample bot.
 */


6
public class Bot : OVCCClient.Client
7
{
8 9
  private MainLoop      loop        = null;
  private bool          interactive = false;
10
  private OVCC.SigQueue sigqueue  = new OVCC.SigQueue ();
11
  
12
  public async bool join (OVCCClient.Server server)
13
    throws Error
14
  {
Colomban Wendling's avatar
Colomban Wendling committed
15
    uint player_suffix = 7;
16
    int table_to_join = -1;
17

18
    /* connect to the server */
19
    yield bind_to (server);
20 21 22

    /* login to the server, choosing an available nickname */
    debug ("Trying to login...");
Colomban Wendling's avatar
Colomban Wendling committed
23
    while (true) {
24
      try {
25
        yield login ("""Clever bot %03u""".printf (player_suffix), "xxx");
26
        break;
27
      } catch (OVCCClient.ServerError.NICKNAME_IN_USE e1) {
28
        player_suffix++;
29
      } catch (OVCCClient.ServerError.MISSING_AUTHENTICATION e2) {
30
        leave ();
31 32
        throw e2; /* avoid infinite loop */
      } catch (Error e3) {
33
        leave ();
34
        throw e3;
35 36
      }
    }
37 38
    debug ("Logged in with player_suffix = %u", player_suffix);

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
    /* join an open table */

    var list = yield server.enumerate_tables(OVCCClient.TablesFilter.OPEN);
    print ("List of open tables:\n");
    var idx = 0;
    foreach (var d in list) {
      print ("  - %02i: %s\n", idx, d.to_string());
      idx++;
    }
    if (list.length == 0) {
      print ("(currently no table)\n");
    }

    print ("What table index to join? (-1 or empty for any open table) ");
    string? line = interactive ? stdin.read_line () : "";
    if (line != null && line != "") {
      table_to_join = int.parse(line);
    }

    debug ("Trying to join table %i...", table_to_join);

60
    try {
61
      yield join_table (table_to_join);
62
    } catch (Error e4) {
63
      leave ();
64
      throw e4;
65 66
    }
    debug ("Joined");
67

68
    /* react on game state changes */
69
    sigqueue.add (game, game.notify["state"].connect ((s, p) => {
Colomban Wendling's avatar
Colomban Wendling committed
70
      switch (((OVCC.Game)s).state) {
71 72 73
        case OVCC.GameState.STARTED:
          debug ("Game started!");
          break;
74
        case OVCC.GameState.FINISHED:
75 76 77 78 79
          try {
            leave ();
          } catch (Error e) {
            warning ("Could not leave: %s", e.message);
          }
80 81 82
          debug ("We're done, bye!");
          break;
        case OVCC.GameState.ABORTED:
83 84 85 86 87
          try {
            leave ();
          } catch (Error e) {
            warning ("Could not leave: %s", e.message);
          }
88 89 90
          debug ("Abnormal game termination!");
          break;
      }
91
    }));
92
    /* we should place a tile? OK, why not */
93
    sigqueue.add (this, place_tile.connect ((tile) => {
94
      debug ("Seems we should place a tile...");
95
      game.board.foreach ((b, p, t) => {
96 97 98 99 100 101 102 103 104 105 106
        for (var i = 0; i < 4; i++) {
          var npos = p;
          
          switch (i) {
            case 0: npos.y--; break; /* top */
            case 1: npos.x++; break; /* right */
            case 2: npos.y++; break; /* bottom */
            case 3: npos.x--; break; /* left */
          }
          for (var j = 0; j < 4; j++) {
            /* FIXME: this should probably be asynchronous... */
107
            if (player.place_tile (npos)) {
Jonathan Michalon's avatar
Jonathan Michalon committed
108 109
              debug ("Placed tile ID %u at %d %d angle %u", tile.id,
                     npos.x, npos.y, tile.rotation);
110 111 112 113 114 115 116 117
              return false;
            } else {
              tile.rotate (1);
            }
          }
        }
        return true;
      });
118
    }));
119
    /* we should place a pawn? let's do so */
Colomban Wendling's avatar
Colomban Wendling committed
120
    sigqueue.add (this, place_pawn.connect ((pos) => {
121
      debug ("We're asked to place a pawn!");
Colomban Wendling's avatar
Colomban Wendling committed
122 123 124 125 126
      var tile = game.board.get_tile (pos);
      if (tile == null) {
        critical ("We were asked to place a pawn somewhere there is no tile");
        return;
      }
127 128 129 130 131 132 133 134 135 136 137 138
      var kind = OVCC.PawnKind.NORMAL;
      if (player.get_remaining_pawn_count (kind) == 0) {
        kind = OVCC.PawnKind.DOUBLE;
      }
      if (player.get_remaining_pawn_count (kind) == 0) {
        debug ("No pawn available");
      } else {
        foreach (var o in tile.objects) {
          if (player.place_pawn (kind, o, pos)) {
            debug ("Placed pawn at %d %d", pos.x, pos.y);
            break;
          }
Colomban Wendling's avatar
Colomban Wendling committed
139
        }
140
      }
141
      game.turn_finished ();
142
    }));
143

144

145
    return true;
146
  }
147

148 149 150 151 152
  public void play ()
    throws OVCC.GameError
  {
    debug ("Requesting game start");
    try {
153
      game.start();
154
    } catch (OVCC.GameError.STARTED e) {
155
      warning ("Cannot start game: %s", e.message);
156 157 158
    }
  }

159
  public new void leave ()
160
    throws OVCCClient.ServerError
161
  {
162 163
    sigqueue.remove_all ();
    
164
    base.leave ();
165 166
    loop.quit ();
  }
167
  
168
  public Bot (MainLoop l, bool i = false)
169 170
  {
    loop = l;
171
    interactive = i;
172
  }
173 174
}

175 176 177 178 179 180

#if HAVE_POSIX
private static void signal_handler (int sig)
{
  debug ("Caught signal %d, exiting", sig);
  try {
181
    bot.leave ();
182
  } catch (Error e) {
183
    warning ("Error leaving from server: %s", e.message);
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
  }
  Posix.exit (2);
}

private static void setup_signal_handlers ()
{
  Posix.sigaction_t s = {};
  s.sa_handler = signal_handler;
  Posix.sigemptyset (s.sa_mask);
  s.sa_flags = 0;
  
  Posix.sigaction (Posix.SIGQUIT, s, null);
  Posix.sigaction (Posix.SIGTERM, s, null);
  Posix.sigaction (Posix.SIGINT, s, null);
}
#endif


Colomban Wendling's avatar
Colomban Wendling committed
202
Bot bot = null;
203

204 205 206 207 208
// has to be outside of main to be const ?!
unowned string  host        = "localhost";
uint16          port        = 0xDEAD;
bool            interactive = false;

209
public int main (string[] args)
210
{
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
  const OptionEntry[] options = {
    { "server", 's', 0, OptionArg.STRING, ref host, "Use this server", "SERVER" },
    { "port", 'p', 0, OptionArg.INT, ref port, "Use this port number", "PORT" },
    { "interactive", 'i', 0, OptionArg.NONE, ref interactive, "Be interactive", null },
    { null }
  };

  try {
    var opt_context = new OptionContext ("- OVCC Bot");
    opt_context.set_help_enabled (true);
    opt_context.add_main_entries (options, null);
    opt_context.parse (ref args);
  } catch (OptionError e) {
    print ("error: %s\n", e.message);
    print ("Run '%s --help' to see a full list of available command line options.\n", args[0]);
    return 0;
227 228
  }
  
Colomban Wendling's avatar
Colomban Wendling committed
229 230
  var loop = new MainLoop ();
  var server = new OVCCClient.Server (host, port);
231
  bot = new Bot (loop, interactive);
232 233 234 235
  
#if HAVE_POSIX
  setup_signal_handlers ();
#endif
236 237 238 239 240 241 242 243 244 245 246 247 248

  bot.join.begin (server, (o, r) => {
    try {
      bot.join.end (r);
      /* wait to let others join */
      Timeout.add_seconds (5, () => {
        if (bot.game.state != OVCC.GameState.STARTED) {
          debug ("let the show begin!");
          try {
            bot.play ();
          } catch (Error e) {
            warning ("Failed to start playing: %s", e.message);
          }
249
        }
250 251 252 253 254 255 256 257
        return false;
      });
    } catch (Error e) {
      warning ("Failed to join on the server: %s", e.message);
      loop.quit ();
    }
  });
  loop.run ();
258 259

  return 0;
260
}