git.haldean.org grandmaster / 989fc3c
add end_game API to protocol Haldean 3 years ago
6 changed file(s) with 152 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
101101 }
102102
103103 The response structure for an invalid move is:
104
105 {
106 "state": null,
107 "error": description of error (string),
108 }
109
110 --------------------------------------------------------------
111 kind = "end_game"
112
113 To voluntarily end a game, send a request with kind set to
114 "end_game". The full structure of the request is:
115
116 {
117 "kind": "end_game",
118 "player": player ID (long int),
119 "game_id": game ID (long int),
120 "termination": termination type (string),
121 }
122
123 Valid termination strings are a subset of the termination
124 types described in the game state section below. The valid
125 voluntary terminations are:
126
127 "taken_draw_white"
128 "taken_draw_black"
129 "resignation_white"
130 "resignation_black"
131
132 Note that "taken_draw_white" and "taken_draw_black" are only
133 valid if the game is in a state in which there are draws
134 available, i.e., if the "draws" constant in the game state is
135 not zero.
136
137 The response structure for a successful end-game is:
138
139 {
140 "state": game state (see below),
141 "error": null,
142 }
143
144 The response structure for an invalid end-game is:
104145
105146 {
106147 "state": null,
7878 resp = handle_move(gt, req);
7979 } else if (strncmp(req_kind, "game_from_pgn", req_kind_len) == 0) {
8080 resp = handle_game_from_pgn(gt, req);
81 } else if (strncmp(req_kind, "end_game", req_kind_len) == 0) {
82 resp = handle_end_game(gt, req);
8183 } else {
8284 resp = json_pack("{ss}", "error", "unknown kind");
8385 }
119119 }
120120 return json_pack("{snss}", "state", "error", "could not perform move");
121121 }
122
123 json_t *
124 handle_end_game(struct game_tree *gt, json_t *req)
125 {
126 game_id_t game_id;
127 player_id_t player;
128 termination_t termination;
129 json_t *t;
130 bool success;
131 struct game *game;
132 color_t player_color;
133
134 get(t, req, "player");
135 player = json_integer_value(t);
136
137 get(t, req, "game_id");
138 game_id = json_integer_value(t);
139
140 get(t, req, "termination");
141 termination = termination_from_str(json_string_value(t));
142
143 if (get_game(gt, game_id) == NULL) {
144 return json_pack("{snss}", "state", "error", "game does not exist");
145 }
146 if (termination == INVALID) {
147 return json_pack("{snss}", "state", "error", "invalid termination");
148 }
149
150 game = get_game(gt, game_id);
151 if (player == game->player_white)
152 player_color = WHITE;
153 else if (player == game->player_black)
154 player_color = BLACK;
155 else
156 return json_pack("{snss}", "state", "error", "invalid player");
157
158 switch (termination)
159 {
160 case TAKEN_DRAW_WHITE:
161 case TAKEN_DRAW_BLACK:
162 if (game->current->move->player == player_color)
163 return json_pack(
164 "{snss}", "state", "error",
165 "player can only take draw on their move");
166 if (!game->current->move->post_board->draws)
167 return json_pack(
168 "{snss}", "state", "error", "no draws available");
169 case RESIGNATION_WHITE:
170 case RESIGNATION_BLACK:
171 if (player_color == WHITE) {
172 if (termination == TAKEN_DRAW_BLACK ||
173 termination == RESIGNATION_BLACK)
174 return json_pack(
175 "{snss}", "state", "error",
176 "cannot end game for other player");
177 } else {
178 if (termination == TAKEN_DRAW_WHITE ||
179 termination == RESIGNATION_WHITE)
180 return json_pack(
181 "{snss}", "state", "error",
182 "cannot end game for other player");
183 }
184 success = end_game(gt, game_id, termination);
185 if (success) {
186 return json_pack(
187 "{sosn}", "state", game_state(gt, game_id), "error");
188 }
189 return json_pack("{snss}", "state", "error", "unknown error");
190
191 default:
192 return json_pack(
193 "{snss}", "state",
194 "error", "provided termination cannot be voluntary");
195 }
196 }
6666 RESIGNATION_WHITE = 0x86,
6767 /* black has resigned, white wins by default */
6868 RESIGNATION_BLACK = 0x87,
69 /* sentinel invalid termination value */
70 INVALID = 0xFF,
6971 } termination_t;
7072
7173 #define TERM_GAME_OVER_MASK 0x80
218220 char *
219221 termination_str(termination_t term);
220222
223 /* Termination state from a NUL-terminated string representation. */
224 termination_t
225 termination_from_str(const char *);
226
221227 #endif
4141
4242 json_t *
4343 handle_game_from_pgn(struct game_tree *gt, json_t *req);
44
45 json_t *
46 handle_end_game(struct game_tree *gt, json_t *req);
2323 #include <assert.h>
2424 #include <jansson.h>
2525 #include <stdio.h>
26 #include <string.h>
2627
2728 #define json_set json_object_set_new_nocheck
2829 #define json_bool(x) ((x) ? json_true() : json_false())
7980 }
8081 }
8182
83 termination_t
84 termination_from_str(const char *str)
85 {
86 size_t str_len;
87 str_len = strlen(str);
88 if (strncmp("available_move", str, str_len) == 0)
89 return AVAILABLE_MOVE;
90 if (strncmp("victory_white", str, str_len) == 0)
91 return VICTORY_WHITE;
92 if (strncmp("victory_black", str, str_len) == 0)
93 return VICTORY_BLACK;
94 if (strncmp("stalemate", str, str_len) == 0)
95 return STALEMATE;
96 if (strncmp("taken_draw_white", str, str_len) == 0)
97 return TAKEN_DRAW_WHITE;
98 if (strncmp("taken_draw_black", str, str_len) == 0)
99 return TAKEN_DRAW_BLACK;
100 if (strncmp("resignation_white", str, str_len) == 0)
101 return RESIGNATION_WHITE;
102 if (strncmp("resignation_black", str, str_len) == 0)
103 return RESIGNATION_BLACK;
104 return INVALID;
105 }
106
82107 void
83108 print_move(const struct move *move)
84109 {