1 module vindinium.comms;
2 
3 /**
4  * Server communications module
5  */
6 
7 private {
8     import vindinium;
9     import stdx.data.json;
10 
11     import std.net.curl;
12     import std.string;
13     import std.stdio;
14     import std.exception;
15 }
16 
17 enum Flags {
18     Get = 0x1,
19     Set = 0x2,
20     Both = 0x3
21 }
22 
23 struct Vindinium {
24     /// Game modes
25     enum Mode {
26         Training,
27         Arena
28     }
29 
30     /// The AI's private API key
31     string key;
32 
33     /// Server to play the game on
34     const string server;
35 
36     /// Mode that the hero is playing in
37     Mode mode;
38 
39     /// Number of turns to play the game
40     uint turns;
41 
42     /// Name of the map to play: "m{1..6}"
43     string map;
44 
45     // Connection shared between game instances
46     private HTTP conn;
47 
48     this(string key, string server, Mode mode, uint turns, string map) {
49         this.conn = HTTP();
50         this.key = key;
51         this.server = server;
52         this.mode = mode;
53         this.turns = turns;
54         this.map = map;
55 
56         conn.addRequestHeader("Content-Type", "application/x-www-form-urlencoded");
57         conn.setUserAgent("Vindinium-D-Client/0.0.1");
58     }
59 
60     VindiniumGame connect() {
61         string uri;
62         final switch(mode)
63         with(Mode) {
64             case Training:
65                 uri = server ~ "/api/training";
66                 break;
67 
68             case Arena:
69                 uri = server ~ "/api/arena";
70                 break;
71         }
72 
73         string post_data = format("key=%s&turns=%s&map=%s", key, turns, map);
74         string response = post(uri, post_data, conn).assumeUnique;
75 
76         return VindiniumGame(conn, key, response);
77     }
78 }
79 
80 struct VindiniumGame {
81 
82     /// Commands that the hero can be issued
83     enum Command {
84         Stay,
85         North,
86         South,
87         East,
88         West
89     }
90 
91     private {
92         HTTP conn;
93         string key;
94         GameResponse *gr;
95     }
96 
97     package
98     this(ref HTTP conn, string key, string initial_game) {
99         this.conn = conn;
100         this.key = key;
101         this.gr = new GameResponse();
102 
103         parse_response(initial_game);
104     }
105 
106     // delegate to gameresponse
107     string play_url() @property { return gr.play_url; }
108     string view_url() @property { return gr.view_url; }
109     bool   finished() @property { return gr.game.finished; }
110     uint   turn()     @property { return gr.game.turn; }
111 
112     auto state() @property const { return gr; }
113 
114     void send_command(Command cmd)
115     in { assert(!finished); }
116     body {
117         string cmd_str;
118         final switch(cmd)
119         with(Command) {
120             case Stay: cmd_str = "Stay"; break;
121             case North: cmd_str = "North"; break;
122             case South: cmd_str = "South"; break;
123             case East: cmd_str = "East"; break;
124             case West: cmd_str = "West"; break;
125         }
126 
127         auto post_data = format("key=%s&dir=%s", key, cmd_str);
128         string response = post(play_url, post_data, conn).assumeUnique;
129 
130         // TODO: merge changes instead of overwriting the game response
131         parse_response(response);
132     }
133 
134     private void parse_response(string resp) {
135         auto json = parseJSONValue(resp);
136         gr.update_from_json(json);
137     }
138 }