added test5(), refactored simple-gtp.c, added gtp input sanitizer, added game-scoring functions
Minclude/simple-go/simple-go.h
| @@ -6,6 +6,7 @@ | |||
|---|---|---|---|
| 6 | 6 | #include <stdio.h> | |
| 7 | 7 | #include <assert.h> | |
| 8 | 8 | #include <string.h> | |
| 9 | + | #include <cutils/cutils.h> | |
| 9 | 10 | ||
| 10 | 11 | #define BLACK 'b' | |
| 11 | 12 | #define WHITE 'w' | |
| @@ -27,9 +28,19 @@ typedef struct game_state | |||
|---|---|---|---|
| 27 | 28 | { | |
| 28 | 29 | go_board* board; | |
| 29 | 30 | float komi; | |
| 31 | + | unsigned long white_captured; | |
| 32 | + | unsigned long black_captured; | |
| 30 | 33 | bool black_turn; | |
| 31 | 34 | } game_state; | |
| 32 | 35 | ||
| 36 | + | typedef struct go_score | |
| 37 | + | { | |
| 38 | + | Vector* white_groups; | |
| 39 | + | Vector* black_groups; | |
| 40 | + | unsigned long white_points; | |
| 41 | + | unsigned long black_points; | |
| 42 | + | } go_score; | |
| 43 | + | ||
| 33 | 44 | ||
| 34 | 45 | go_board* create_board(go_coordinate size); | |
| 35 | 46 | void delete_board(go_board* board); | |
| @@ -40,15 +51,21 @@ bool check_bounds(const go_board* board, go_coordinate y, go_coordinate x); | |||
|---|---|---|---|
| 40 | 51 | char get_board_at(const go_board* board, go_coordinate y, go_coordinate x); | |
| 41 | 52 | void set_board_at(go_board* board, go_coordinate y, go_coordinate x, char item); | |
| 42 | 53 | ||
| 43 | - | void kill_group(go_board* board, go_board* overlay); | |
| 54 | + | void kill_group(go_board* board, const go_board* overlay); | |
| 44 | 55 | bool group_killable(game_state* game, go_coordinate y, go_coordinate x); | |
| 45 | 56 | bool group_attachable(const game_state* game, go_coordinate y, go_coordinate x); | |
| 46 | 57 | bool play_at(game_state* game, go_coordinate y, go_coordinate x, go_symbol color); | |
| 47 | 58 | ||
| 48 | - | void print_board(go_board* board); | |
| 49 | - | char* board_to_string(go_board* board); | |
| 59 | + | void print_board(const go_board* board); | |
| 60 | + | char* board_to_string(const go_board* board); | |
| 50 | 61 | void find_group(const go_board* board, go_board* overlay, go_coordinate y, go_coordinate x); | |
| 51 | - | unsigned long count_liberties(go_board* board, go_board* overlay); | |
| 62 | + | go_symbol group_belongs(const go_board* board, const go_board* overlay); | |
| 63 | + | unsigned long count_liberties(const go_board* board, const go_board* overlay); | |
| 64 | + | unsigned long group_size(go_board* group); | |
| 65 | + | ||
| 66 | + | go_score* score_game(const game_state* board); | |
| 67 | + | void delete_score(go_score* score); | |
| 68 | + | ||
| 52 | 69 | ||
| 53 | 70 | ||
| 54 | 71 | ||
Ainclude/simple-go/simple-gtp-func.h
| @@ -0,0 +1,29 @@ | |||
|---|---|---|---|
| 1 | + | #ifndef SIMPLE_GTP_FUNC_H | |
| 2 | + | #define SIMPLE_GTP_FUNC_H | |
| 3 | + | #include <string.h> | |
| 4 | + | #include <stdlib.h> | |
| 5 | + | #include <stdio.h> | |
| 6 | + | #include <string.h> | |
| 7 | + | #include <ctype.h> | |
| 8 | + | #include <simple-go/simple-go.h> | |
| 9 | + | #include <cutils/cutils.h> | |
| 10 | + | ||
| 11 | + | extern const char* const known_commands_string ; | |
| 12 | + | extern const char* known_commands_array[]; | |
| 13 | + | ||
| 14 | + | char* protocol_version_func(const Vector* arguments, const char* id, game_state* game); | |
| 15 | + | char* name_func(const Vector* arguments, const char* id, game_state* game); | |
| 16 | + | char* version_func(const Vector* arguments, const char* id, game_state* game); | |
| 17 | + | char* known_command_func(const Vector* arguments, const char* id, game_state* game); | |
| 18 | + | char* list_commands_func(const Vector* arguments, const char* id, game_state* game); | |
| 19 | + | char* boardsize_func(const Vector* arguments, const char* id, game_state* game); | |
| 20 | + | char* komi_func(const Vector* arguments, const char* id, game_state* game); | |
| 21 | + | char* clear_board_func(const Vector* arguments, const char* id, game_state* game); | |
| 22 | + | char* play_func(const Vector* arguments, const char* id, game_state* game); | |
| 23 | + | char* genmove_func(const Vector* arguments, const char* id, game_state* game); | |
| 24 | + | char* show_board_func(const Vector* arguments, const char* id, game_state* game); | |
| 25 | + | char* final_score_func(const Vector* arguments, const char* id, game_state* game); | |
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | #endif //SIMPLE_GTP_FUNC_H | |
Mmeson.build
| @@ -1,9 +1,9 @@ | |||
|---|---|---|---|
| 1 | 1 | project('simple-go', 'c') | |
| 2 | 2 | ||
| 3 | - | CFLAGS = ['-std=c11', '-fstrict-aliasing', '-Wall', '-Wpedantic', '-Wextra'] | |
| 3 | + | CFLAGS = ['-std=c11', '-fstrict-aliasing', '-Wall', '-Wpedantic', '-Wextra', '-Wno-unused-parameter'] | |
| 4 | 4 | ||
| 5 | 5 | cutils_sp = subproject('cutils') | |
| 6 | - | dep = cutils_sp.get_variable('cutils_dep') | |
| 6 | + | dep = dependency('cutils', fallback : ['cutils', 'cutils_dep']) | |
| 7 | 7 | ||
| 8 | 8 | subdir('include') | |
| 9 | 9 | subdir('src') | |
Asrc/gtp-start.c
| @@ -0,0 +1,21 @@ | |||
|---|---|---|---|
| 1 | + | #include <simple-go/simple-go.h> | |
| 2 | + | #include <simple-go/simple-gtp.h> | |
| 3 | + | ||
| 4 | + | int main(int argc, char** argv) | |
| 5 | + | { | |
| 6 | + | game_state* game = create_game(19, 6.5); | |
| 7 | + | char input[50] = {0}; | |
| 8 | + | char* answer; | |
| 9 | + | ||
| 10 | + | while(1) | |
| 11 | + | { | |
| 12 | + | fgets(input, 49, stdin); | |
| 13 | + | if(strcmp(input, "quit\n") == 0) | |
| 14 | + | break; | |
| 15 | + | answer = handle_gtp_cmd(input, game); | |
| 16 | + | printf("%s", answer); | |
| 17 | + | free(answer); | |
| 18 | + | } | |
| 19 | + | ||
| 20 | + | delete_game(game); | |
| 21 | + | } | |
Msrc/meson.build
| @@ -1,6 +1,7 @@ | |||
|---|---|---|---|
| 1 | 1 | cutils_sp = subproject('cutils') | |
| 2 | 2 | dep = cutils_sp.get_variable('cutils_dep') | |
| 3 | 3 | ||
| 4 | - | src = ['simple-go.c', 'simple-gtp.c'] | |
| 4 | + | src = ['simple-go.c', 'simple-gtp.c', 'simple-gtp-func.c'] | |
| 5 | 5 | ||
| 6 | - | executable('test_go', src + ['test.c'], include_directories : inc, dependencies : dep, c_args: CFLAGS ) | |
| 6 | + | executable('test_go', src + ['test.c'], include_directories : inc, dependencies : dep, c_args: CFLAGS ) | |
| 7 | + | executable('gtp-start', src + ['gtp-start.c'], include_directories : inc, dependencies : dep, c_args: CFLAGS ) | |
Msrc/simple-go.c
| @@ -27,6 +27,8 @@ game_state* create_game(go_coordinate size, float komi) | |||
|---|---|---|---|
| 27 | 27 | game->board = create_board(size); | |
| 28 | 28 | game->black_turn = true; | |
| 29 | 29 | game->komi = komi; | |
| 30 | + | game->white_captured = 0; | |
| 31 | + | game->black_captured = 0; | |
| 30 | 32 | ||
| 31 | 33 | return game; | |
| 32 | 34 | } | |
| @@ -45,7 +47,7 @@ bool check_bounds(const go_board* board, go_coordinate y, go_coordinate x) | |||
|---|---|---|---|
| 45 | 47 | return false; | |
| 46 | 48 | } | |
| 47 | 49 | ||
| 48 | - | void kill_group(go_board* board, go_board* overlay) | |
| 50 | + | void kill_group(go_board* board, const go_board* overlay) | |
| 49 | 51 | { | |
| 50 | 52 | for(go_coordinate y = 0; y < board->size; y++) | |
| 51 | 53 | { | |
| @@ -57,7 +59,7 @@ void kill_group(go_board* board, go_board* overlay) | |||
|---|---|---|---|
| 57 | 59 | } | |
| 58 | 60 | } | |
| 59 | 61 | ||
| 60 | - | void print_board(go_board* board) | |
| 62 | + | void print_board(const go_board* board) | |
| 61 | 63 | { | |
| 62 | 64 | for(go_coordinate y = 0; y < board->size; y++) | |
| 63 | 65 | { | |
| @@ -70,7 +72,7 @@ void print_board(go_board* board) | |||
|---|---|---|---|
| 70 | 72 | } | |
| 71 | 73 | } | |
| 72 | 74 | ||
| 73 | - | char* board_to_string(go_board* board) | |
| 75 | + | char* board_to_string(const go_board* board) | |
| 74 | 76 | { | |
| 75 | 77 | size_t str_size = (board->size*2+6)*(board->size+2)+2; | |
| 76 | 78 | char* ret = calloc(str_size+1,1); | |
| @@ -108,10 +110,7 @@ char* board_to_string(go_board* board) | |||
|---|---|---|---|
| 108 | 110 | ret[index++] = '\n'; | |
| 109 | 111 | } | |
| 110 | 112 | ||
| 111 | - | ||
| 112 | - | ||
| 113 | 113 | return ret; | |
| 114 | - | ||
| 115 | 114 | } | |
| 116 | 115 | ||
| 117 | 116 | ||
| @@ -238,7 +237,60 @@ void find_group(const go_board* board, go_board* overlay, go_coordinate y, go_co | |||
|---|---|---|---|
| 238 | 237 | } | |
| 239 | 238 | } | |
| 240 | 239 | ||
| 241 | - | unsigned long count_liberties(go_board* board, go_board* overlay) | |
| 240 | + | go_symbol group_belongs(const go_board* board, const go_board* overlay) | |
| 241 | + | { | |
| 242 | + | assert(board->size == overlay->size); | |
| 243 | + | ||
| 244 | + | go_symbol belongs = EMPTY; | |
| 245 | + | ||
| 246 | + | for(go_coordinate y = 0; y < board->size; y++) | |
| 247 | + | { | |
| 248 | + | for(go_coordinate x = 0; x < board->size; x++) | |
| 249 | + | { | |
| 250 | + | if(get_board_at(overlay, y, x) == GROUP) | |
| 251 | + | { | |
| 252 | + | go_symbol up = get_board_at(board,y-1,x); | |
| 253 | + | go_symbol left = get_board_at(board,y,x-1); | |
| 254 | + | go_symbol down = get_board_at(board,y+1,x); | |
| 255 | + | go_symbol right = get_board_at(board,y,x+1); | |
| 256 | + | ||
| 257 | + | if(up == WHITE || up == BLACK) | |
| 258 | + | { | |
| 259 | + | belongs = (belongs == EMPTY) ? up : (up != belongs ? NO_FIELD : up); | |
| 260 | + | } | |
| 261 | + | if(left == WHITE || left == BLACK) | |
| 262 | + | { | |
| 263 | + | belongs = (belongs == EMPTY) ? left : (left != belongs ? NO_FIELD : left); | |
| 264 | + | } | |
| 265 | + | if(down == WHITE || down == BLACK) | |
| 266 | + | { | |
| 267 | + | belongs = (belongs == EMPTY) ? down : (down != belongs ? NO_FIELD : down); | |
| 268 | + | } | |
| 269 | + | if(right == WHITE || right == BLACK) | |
| 270 | + | { | |
| 271 | + | belongs = (belongs == EMPTY) ? right : (right != belongs ? NO_FIELD : right); | |
| 272 | + | } | |
| 273 | + | } | |
| 274 | + | } | |
| 275 | + | } | |
| 276 | + | return belongs; | |
| 277 | + | } | |
| 278 | + | ||
| 279 | + | unsigned long group_size(go_board* group) | |
| 280 | + | { | |
| 281 | + | unsigned long sum = 0; | |
| 282 | + | for(go_coordinate y = 0; y < group->size; y++) | |
| 283 | + | { | |
| 284 | + | for(go_coordinate x = 0; x < group->size; x++) | |
| 285 | + | { | |
| 286 | + | if(get_board_at(group, y, x) == GROUP) | |
| 287 | + | ++sum; | |
| 288 | + | } | |
| 289 | + | } | |
| 290 | + | return sum; | |
| 291 | + | } | |
| 292 | + | ||
| 293 | + | unsigned long count_liberties(const go_board* board, const go_board* overlay) | |
| 242 | 294 | { | |
| 243 | 295 | assert(board->size == overlay->size); | |
| 244 | 296 | ||
| @@ -283,4 +335,70 @@ unsigned long count_liberties(go_board* board, go_board* overlay) | |||
|---|---|---|---|
| 283 | 335 | return liberties; | |
| 284 | 336 | } | |
| 285 | 337 | ||
| 338 | + | go_score* score_game(const game_state* game) | |
| 339 | + | { | |
| 340 | + | go_score* ret = malloc(sizeof(*ret)); | |
| 341 | + | ||
| 342 | + | ret->white_groups = new_vector(); | |
| 343 | + | ret->black_groups = new_vector(); | |
| 344 | + | ret->white_points = game->white_captured; | |
| 345 | + | ret->black_points = game->black_captured; | |
| 346 | + | ||
| 347 | + | go_board* board = game->board; | |
| 348 | + | go_board* current; | |
| 349 | + | go_symbol belongs; | |
| 350 | + | ||
| 351 | + | for(go_coordinate y = 0; y < board->size; y++) | |
| 352 | + | { | |
| 353 | + | for(go_coordinate x = 0; x < board->size; x++) | |
| 354 | + | { | |
| 355 | + | //if field is empty | |
| 356 | + | if(get_board_at(board, y, x) == EMPTY) | |
| 357 | + | { | |
| 358 | + | //check if field is already counted | |
| 359 | + | for(go_coordinate i = 0; i < ret->white_groups->length; i++) | |
| 360 | + | { | |
| 361 | + | current = vector_at(ret->white_groups, i); | |
| 362 | + | if(get_board_at(current, y, x) == GROUP) | |
| 363 | + | continue; | |
| 364 | + | } | |
| 365 | + | for(go_coordinate i = 0; i < ret->black_groups->length; i++) | |
| 366 | + | { | |
| 367 | + | current = vector_at(ret->black_groups, i); | |
| 368 | + | if(get_board_at(current, y, x) == GROUP) | |
| 369 | + | continue; | |
| 370 | + | } | |
| 371 | + | go_board* overlay = create_board(board->size); | |
| 372 | + | find_group(board, overlay, y, x); | |
| 373 | + | ||
| 374 | + | //check if field belongs to a group | |
| 375 | + | if((belongs = group_belongs(board, overlay)) == WHITE) | |
| 376 | + | { | |
| 377 | + | vector_push(ret->white_groups, overlay); | |
| 378 | + | ret->white_points += group_size(overlay); | |
| 379 | + | } else if(belongs == BLACK) { | |
| 380 | + | vector_push(ret->black_groups, overlay); | |
| 381 | + | ret->black_points += group_size(overlay); | |
| 382 | + | } else { | |
| 383 | + | delete_board(overlay); | |
| 384 | + | } | |
| 385 | + | } | |
| 386 | + | } | |
| 387 | + | } | |
| 388 | + | ||
| 389 | + | return ret; | |
| 390 | + | } | |
| 391 | + | ||
| 392 | + | void delete_board_wrapper(void* board) | |
| 393 | + | { | |
| 394 | + | delete_board(board); | |
| 395 | + | } | |
| 396 | + | ||
| 397 | + | void delete_score(go_score* score) | |
| 398 | + | { | |
| 399 | + | delete_vector(score->white_groups, delete_board_wrapper); | |
| 400 | + | delete_vector(score->black_groups, delete_board_wrapper); | |
| 401 | + | free(score); | |
| 402 | + | } | |
| 403 | + | ||
| 286 | 404 | ||
Asrc/simple-gtp-func.c
| @@ -0,0 +1,170 @@ | |||
|---|---|---|---|
| 1 | + | #include <simple-go/simple-gtp-func.h> | |
| 2 | + | ||
| 3 | + | const char* const known_commands_string = "protocol_version\nname\nversion\nknown_command\nlist_commands\nquit\nboardsize\nclear_board\nkomi\nplay\ngenmove\nshowboard"; | |
| 4 | + | const char* known_commands_array[] = {"protocol_version","name","version","known_command","list_commands","quit","boardsize","clear_board","komi","play","genmove","showboard", NULL}; | |
| 5 | + | ||
| 6 | + | static char* cmd_error(const char* msg, const char* id) | |
| 7 | + | { | |
| 8 | + | char* ret = malloc(strlen("? ") + (msg ? strlen(msg) : 0) + (id ? strlen(id) : 0) + 3); | |
| 9 | + | strcpy(ret, "?"); | |
| 10 | + | ||
| 11 | + | if(id) | |
| 12 | + | strcat(ret, id); | |
| 13 | + | ||
| 14 | + | if(msg) | |
| 15 | + | { | |
| 16 | + | strcat(ret, " "); | |
| 17 | + | strcat(ret, msg); | |
| 18 | + | } | |
| 19 | + | strcat(ret, "\n\n"); | |
| 20 | + | return ret; | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | static char* cmd_success(const char* msg, const char* id) | |
| 24 | + | { | |
| 25 | + | char* ret = malloc(strlen("= ") + (msg ? strlen(msg) : 0) + (id ? strlen(id) : 0) + 3); | |
| 26 | + | strcpy(ret, "="); | |
| 27 | + | ||
| 28 | + | if(id) | |
| 29 | + | strcat(ret, id); | |
| 30 | + | ||
| 31 | + | if(msg) | |
| 32 | + | { | |
| 33 | + | strcat(ret, " "); | |
| 34 | + | strcat(ret, msg); | |
| 35 | + | } | |
| 36 | + | strcat(ret, "\n\n"); | |
| 37 | + | return ret; | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | char* protocol_version_func(const Vector* arguments, const char* id, game_state* game) | |
| 41 | + | { | |
| 42 | + | return cmd_success("2", id); | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | char* name_func(const Vector* arguments, const char* id, game_state* game) | |
| 46 | + | { | |
| 47 | + | return cmd_success("simple-go", id); | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | char* version_func(const Vector* arguments, const char* id, game_state* game) | |
| 51 | + | { | |
| 52 | + | return cmd_success("0.1", id); | |
| 53 | + | } | |
| 54 | + | ||
| 55 | + | char* known_command_func(const Vector* arguments, const char* id, game_state* game) | |
| 56 | + | { | |
| 57 | + | for(size_t i = 0; known_commands_array[i]; i++) | |
| 58 | + | { | |
| 59 | + | if(arguments->length > 0 && strcmp(vector_at(arguments, 0), known_commands_array[i]) == 0) | |
| 60 | + | { | |
| 61 | + | return cmd_success("false", id); | |
| 62 | + | } | |
| 63 | + | } | |
| 64 | + | return cmd_success("false", id); | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | char* list_commands_func(const Vector* arguments, const char* id, game_state* game) | |
| 68 | + | { | |
| 69 | + | return cmd_success(known_commands_string, id); | |
| 70 | + | } | |
| 71 | + | ||
| 72 | + | char* boardsize_func(const Vector* arguments, const char* id, game_state* game) | |
| 73 | + | { | |
| 74 | + | unsigned int size; | |
| 75 | + | if(arguments->length == 1 && sscanf(vector_at(arguments,0), "%u", &size) > 0 && 0 < size && size < 26) | |
| 76 | + | { | |
| 77 | + | delete_board(game->board); | |
| 78 | + | game->board = create_board(size); | |
| 79 | + | return cmd_success(NULL, id); | |
| 80 | + | } else { | |
| 81 | + | return cmd_error("unacceptable size", id); | |
| 82 | + | } | |
| 83 | + | } | |
| 84 | + | ||
| 85 | + | char* komi_func(const Vector* arguments, const char* id, game_state* game) | |
| 86 | + | { | |
| 87 | + | float komi; | |
| 88 | + | if(arguments->length == 1 && sscanf(vector_at(arguments,0), "%f", &komi)) | |
| 89 | + | { | |
| 90 | + | game->komi = komi; | |
| 91 | + | return cmd_success(NULL, id); | |
| 92 | + | } else { | |
| 93 | + | return cmd_error("komi not a float", id); | |
| 94 | + | } | |
| 95 | + | } | |
| 96 | + | ||
| 97 | + | char* clear_board_func(const Vector* arguments, const char* id, game_state* game) | |
| 98 | + | { | |
| 99 | + | unsigned int size = game->board->size; | |
| 100 | + | delete_board(game->board); | |
| 101 | + | game->board = create_board(size); | |
| 102 | + | return cmd_success(NULL, id); | |
| 103 | + | } | |
| 104 | + | ||
| 105 | + | char* play_func(const Vector* arguments, const char* id, game_state* game) | |
| 106 | + | { | |
| 107 | + | char* color = malloc(6); | |
| 108 | + | char x; | |
| 109 | + | int y; | |
| 110 | + | if((arguments->length == 2 || arguments->length == 3) && //either its "a 10" or "a10" | |
| 111 | + | (snprintf(color, 6, "%s", (char*)vector_at(arguments,0)) > 0) && //"white" and "black" are only 6 chars | |
| 112 | + | (strcmp(color, "white") == 0 || strcmp(color, "black") == 0 || | |
| 113 | + | strcmp(color, "w") == 0 || strcmp(color, "b") == 0) && // check color | |
| 114 | + | (sscanf(vector_at(arguments,1), "%c", &x) > 0) && // get horizontal coordinate | |
| 115 | + | ((unsigned char)(tolower(x) - 'a') < game->board->size) && // check range | |
| 116 | + | ((sscanf((char*)vector_at(arguments,1)+1, "%d", &y) > 0) || // get vertical coordinate if no space between | |
| 117 | + | ((arguments->length == 3) && (sscanf((char*)vector_at(arguments,2), "%d", &y) > 0))) && // get vertical coordinate if space between | |
| 118 | + | ((unsigned int)y <= game->board->size) && y > 0) // check range | |
| 119 | + | { | |
| 120 | + | ||
| 121 | + | if(play_at(game, game->board->size-(unsigned int)y, (unsigned char)(x-'a'), (strcmp(color, "white") == 0 || strcmp(color, "w") == 0) ? WHITE : BLACK)) | |
| 122 | + | { | |
| 123 | + | free(color); | |
| 124 | + | return cmd_success(NULL, id); | |
| 125 | + | } else { | |
| 126 | + | free(color); | |
| 127 | + | return cmd_error("illegal move", id); | |
| 128 | + | } | |
| 129 | + | } else { | |
| 130 | + | free(color); | |
| 131 | + | return cmd_error("invalid color or coordinate", id); | |
| 132 | + | } | |
| 133 | + | ||
| 134 | + | } | |
| 135 | + | ||
| 136 | + | char* genmove_func(const Vector* arguments, const char* id, game_state* game) | |
| 137 | + | { | |
| 138 | + | char* color = malloc(6); | |
| 139 | + | if(arguments->length == 1 && | |
| 140 | + | (snprintf(color, 6, "%s", (char*)vector_at(arguments,0)) > 0) && | |
| 141 | + | (strcmp(color, "white") == 0 || strcmp(color, "black") == 0 || | |
| 142 | + | strcmp(color, "w") == 0 || strcmp(color, "b") == 0 )) | |
| 143 | + | { | |
| 144 | + | go_coordinate y; | |
| 145 | + | go_coordinate x; | |
| 146 | + | do | |
| 147 | + | { | |
| 148 | + | y = (go_coordinate)rand() % game->board->size; | |
| 149 | + | x = (go_coordinate)rand() % game->board->size; | |
| 150 | + | } while(!play_at(game, y, x, (strcmp(color, "white") == 0 || strcmp(color, "w") == 0) ? WHITE : BLACK)); | |
| 151 | + | free(color); | |
| 152 | + | return cmd_success(NULL, id); | |
| 153 | + | } else { | |
| 154 | + | free(color); | |
| 155 | + | return cmd_error("invalid color or coordinate", id); | |
| 156 | + | } | |
| 157 | + | } | |
| 158 | + | ||
| 159 | + | char* show_board_func(const Vector* arguments, const char* id, game_state* game) | |
| 160 | + | { | |
| 161 | + | char* board = board_to_string(game->board); | |
| 162 | + | char* tmp = cmd_success(board, id); | |
| 163 | + | free(board); | |
| 164 | + | return tmp; | |
| 165 | + | } | |
| 166 | + | ||
| 167 | + | char* final_score_func(const Vector* arguments, const char* id, game_state* game) | |
| 168 | + | { | |
| 169 | + | return NULL; | |
| 170 | + | } | |
Msrc/simple-gtp.c
| @@ -1,4 +1,5 @@ | |||
|---|---|---|---|
| 1 | 1 | #include <simple-go/simple-gtp.h> | |
| 2 | + | #include <simple-go/simple-gtp-func.h> | |
| 2 | 3 | ||
| 3 | 4 | typedef struct msg_formatted | |
| 4 | 5 | { | |
| @@ -7,39 +8,6 @@ typedef struct msg_formatted | |||
|---|---|---|---|
| 7 | 8 | char* id; | |
| 8 | 9 | } msg_formatted; | |
| 9 | 10 | ||
| 10 | - | char* cmd_error(const char* msg, char* id) | |
| 11 | - | { | |
| 12 | - | char* ret = malloc(strlen("? ") + strlen(msg) + (id ? strlen(id) : 0) + 3); | |
| 13 | - | strcpy(ret, "?"); | |
| 14 | - | if(id) | |
| 15 | - | { | |
| 16 | - | strcat(ret, id); | |
| 17 | - | free(id); | |
| 18 | - | } | |
| 19 | - | strcat(ret, " "); | |
| 20 | - | strcat(ret, msg); | |
| 21 | - | strcat(ret, "\n\n"); | |
| 22 | - | return ret; | |
| 23 | - | } | |
| 24 | - | ||
| 25 | - | char* cmd_success(const char* msg, char* id) | |
| 26 | - | { | |
| 27 | - | char* ret = malloc(strlen("= ") + strlen(msg) + (id ? strlen(id) : 0) + 3); | |
| 28 | - | strcpy(ret, "="); | |
| 29 | - | if(id) | |
| 30 | - | { | |
| 31 | - | strcat(ret, id); | |
| 32 | - | free(id); | |
| 33 | - | } | |
| 34 | - | strcat(ret, " "); | |
| 35 | - | strcat(ret, msg); | |
| 36 | - | strcat(ret, "\n\n"); | |
| 37 | - | return ret; | |
| 38 | - | } | |
| 39 | - | ||
| 40 | - | const char* const known_commands_string = "protocol_version\nname\nversion\nknown_command\nlist_commands\nquit\nboardsize\nclear_board\nkomi\nplay\ngenmove\nshowboard"; | |
| 41 | - | const char* known_commands_array[] = {"protocol_version","name","version","known_command","list_commands","quit","boardsize","clear_board","komi","play","genmove","showboard"}; | |
| 42 | - | int cmd_count = sizeof(known_commands_array)/sizeof(known_commands_array[0]); | |
| 43 | 11 | ||
| 44 | 12 | static msg_formatted format_msg(const char* msg) | |
| 45 | 13 | { | |
| @@ -85,6 +53,27 @@ static msg_formatted format_msg(const char* msg) | |||
|---|---|---|---|
| 85 | 53 | return formatted; | |
| 86 | 54 | } | |
| 87 | 55 | ||
| 56 | + | static char* sanitize(const char* msg) | |
| 57 | + | { | |
| 58 | + | char* ret = calloc(strlen(msg)+1, 1); | |
| 59 | + | ||
| 60 | + | for(size_t i = 0, reti = 0; i < strlen(msg); i++) | |
| 61 | + | { | |
| 62 | + | if(msg[i] == '\t') | |
| 63 | + | { | |
| 64 | + | ret[reti++] = ' '; | |
| 65 | + | } else if(iscntrl(msg[i])){ | |
| 66 | + | ++reti; | |
| 67 | + | } else if(msg[i] == '#'){ | |
| 68 | + | break; | |
| 69 | + | } else { | |
| 70 | + | ret[reti++] = msg[i]; | |
| 71 | + | } | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | return ret; | |
| 75 | + | } | |
| 76 | + | ||
| 88 | 77 | char* handle_gtp_cmd(const char* msg, game_state* game) | |
| 89 | 78 | { | |
| 90 | 79 | if(!msg || !strlen(msg)) | |
| @@ -92,116 +81,47 @@ char* handle_gtp_cmd(const char* msg, game_state* game) | |||
|---|---|---|---|
| 92 | 81 | char* ret = calloc(1,1); | |
| 93 | 82 | return ret; | |
| 94 | 83 | } | |
| 84 | + | char* msg_san = sanitize(msg); | |
| 95 | 85 | ||
| 96 | - | msg_formatted formatted = format_msg(msg); | |
| 86 | + | msg_formatted formatted = format_msg(msg_san); | |
| 97 | 87 | char* command = formatted.command; | |
| 98 | 88 | Vector* arguments = formatted.arguments; | |
| 99 | 89 | char* id = formatted.id; | |
| 100 | 90 | ||
| 101 | - | char* (*func_ptr)(const char*, char* id) = cmd_error; | |
| 102 | - | const char* func_args = "unknown command"; | |
| 91 | + | char* ret; | |
| 92 | + | char* (*func_ptr)(const Vector*, const char*, game_state*); | |
| 103 | 93 | ||
| 104 | 94 | if(strcmp(command, "protocol_version") == 0) | |
| 105 | 95 | { | |
| 106 | - | func_ptr = cmd_success; | |
| 107 | - | func_args = "2"; | |
| 96 | + | func_ptr = protocol_version_func; | |
| 108 | 97 | } else if(strcmp(command, "name") == 0) { | |
| 109 | - | func_ptr = cmd_success; | |
| 110 | - | func_args = "simple-go"; | |
| 98 | + | func_ptr = name_func; | |
| 111 | 99 | } else if(strcmp(command, "version") == 0) { | |
| 112 | - | func_ptr = cmd_success; | |
| 113 | - | func_args = "0.1"; | |
| 100 | + | func_ptr = version_func; | |
| 114 | 101 | } else if(strcmp(command, "known_command") == 0) { | |
| 115 | - | func_ptr = cmd_success; | |
| 116 | - | func_args = "false"; | |
| 117 | - | for(int i = 0; i < cmd_count; i++) | |
| 118 | - | { | |
| 119 | - | if(arguments->length > 0 && strcmp(vector_at(arguments, 0), known_commands_array[i]) == 0) | |
| 120 | - | { | |
| 121 | - | func_args = "true"; | |
| 122 | - | break; | |
| 123 | - | } | |
| 124 | - | } | |
| 102 | + | func_ptr = known_command_func; | |
| 125 | 103 | } else if(strcmp(command, "list_commands") == 0) { | |
| 126 | - | func_ptr = cmd_success; | |
| 127 | - | func_args = known_commands_string; | |
| 104 | + | func_ptr = list_commands_func; | |
| 128 | 105 | } else if(strcmp(command, "boardsize") == 0) { | |
| 129 | - | unsigned int size; | |
| 130 | - | if(arguments->length == 1 && sscanf(vector_at(arguments,0), "%u", &size) > 0 && 0 < size && size < 26) | |
| 131 | - | { | |
| 132 | - | func_ptr = cmd_success; | |
| 133 | - | func_args = ""; | |
| 134 | - | delete_board(game->board); | |
| 135 | - | game->board = create_board(size); | |
| 136 | - | } else { | |
| 137 | - | func_ptr = cmd_error; | |
| 138 | - | func_args = "unacceptable size"; | |
| 139 | - | } | |
| 106 | + | func_ptr = boardsize_func; | |
| 140 | 107 | } else if(strcmp(command, "komi") == 0) { | |
| 141 | - | float komi; | |
| 142 | - | if(arguments->length == 1 && sscanf(vector_at(arguments,0), "%f", &komi)) | |
| 143 | - | { | |
| 144 | - | func_ptr = cmd_success; | |
| 145 | - | func_args = ""; | |
| 146 | - | game->komi = komi; | |
| 147 | - | } else { | |
| 148 | - | func_ptr = cmd_error; | |
| 149 | - | func_args = "komi not a float"; | |
| 150 | - | } | |
| 108 | + | func_ptr = komi_func; | |
| 151 | 109 | } else if(strcmp(command, "clear_board") == 0) { | |
| 152 | - | func_ptr = cmd_success; | |
| 153 | - | func_args = ""; | |
| 154 | - | unsigned int size = game->board->size; | |
| 155 | - | delete_board(game->board); | |
| 156 | - | game->board = create_board(size); | |
| 110 | + | func_ptr = clear_board_func; | |
| 157 | 111 | } else if(strcmp(command, "play") == 0) { | |
| 158 | - | char* color = malloc(6); | |
| 159 | - | char x; | |
| 160 | - | int y; | |
| 161 | - | if((arguments->length == 2 || arguments->length == 3) && //either its "a 10" or "a10" | |
| 162 | - | (snprintf(color, 6, "%s", (char*)vector_at(arguments,0)) > 0) && //"white" and "black" are only 6 chars | |
| 163 | - | (strcmp(color, "white") == 0 || strcmp(color, "black") == 0) && // check color | |
| 164 | - | (sscanf(vector_at(arguments,1), "%c", &x) > 0) && // get horizontal coordinate | |
| 165 | - | ((unsigned char)(tolower(x) - 'a') < game->board->size) && // check range | |
| 166 | - | ((sscanf((char*)vector_at(arguments,1)+1, "%d", &y) > 0) || // get vertical coordinate if no space between | |
| 167 | - | ((arguments->length == 3) && (sscanf((char*)vector_at(arguments,2), "%d", &y) > 0))) && // get vertical coordinate if space between | |
| 168 | - | ((unsigned int)y <= game->board->size) && y > 0) // check range | |
| 169 | - | { | |
| 170 | - | if(play_at(game, game->board->size-(unsigned int)y, (unsigned char)(x-'a'), strcmp(color, "white") == 0 ? WHITE : BLACK)) | |
| 171 | - | { | |
| 172 | - | func_ptr = cmd_success; | |
| 173 | - | func_args = ""; | |
| 174 | - | } else { | |
| 175 | - | func_ptr = cmd_error; | |
| 176 | - | func_args = "illegal move"; | |
| 177 | - | } | |
| 178 | - | } else { | |
| 179 | - | func_ptr = cmd_error; | |
| 180 | - | func_args = "invalid color or coordinate"; | |
| 181 | - | } | |
| 182 | - | free(color); | |
| 112 | + | func_ptr = play_func; | |
| 183 | 113 | } else if(strcmp(command, "genmove") == 0) { | |
| 184 | - | func_ptr = cmd_success; | |
| 185 | - | func_args = ""; | |
| 186 | - | go_coordinate y; | |
| 187 | - | go_coordinate x; | |
| 188 | - | do | |
| 189 | - | { | |
| 190 | - | y = (go_coordinate)rand() % game->board->size; | |
| 191 | - | x = (go_coordinate)rand() % game->board->size; | |
| 192 | - | } while(!play_at(game, y, x, NO_FIELD)); | |
| 114 | + | func_ptr = genmove_func; | |
| 193 | 115 | } else if(strcmp(command, "showboard") == 0) { | |
| 194 | - | char* board = board_to_string(game->board); | |
| 195 | - | ||
| 196 | - | free(command); | |
| 197 | - | delete_vector(arguments, free); | |
| 198 | - | char* tmp = cmd_success(board, id); | |
| 199 | - | free(board); | |
| 200 | - | return tmp; | |
| 116 | + | func_ptr = show_board_func; | |
| 201 | 117 | } | |
| 202 | 118 | ||
| 119 | + | ret = func_ptr(arguments, id, game); | |
| 120 | + | ||
| 203 | 121 | free(command); | |
| 122 | + | free(msg_san); | |
| 123 | + | free(id); | |
| 204 | 124 | delete_vector(arguments, free); | |
| 205 | 125 | ||
| 206 | - | return func_ptr(func_args, id); | |
| 126 | + | return ret; | |
| 207 | 127 | } | |
Msrc/test.c
| @@ -138,20 +138,46 @@ void test4(void) | |||
|---|---|---|---|
| 138 | 138 | puts("test4 finished"); | |
| 139 | 139 | } | |
| 140 | 140 | ||
| 141 | + | void test5(void) | |
| 142 | + | { | |
| 143 | + | puts("test5 running"); | |
| 144 | + | game_state* game = create_game(19,0); | |
| 145 | + | set_board_at(game->board, 2, 3, WHITE); | |
| 146 | + | set_board_at(game->board, 2, 5, WHITE); | |
| 147 | + | set_board_at(game->board, 1, 4, WHITE); | |
| 148 | + | set_board_at(game->board, 3, 4, WHITE); | |
| 149 | + | ||
| 150 | + | set_board_at(game->board, 5, 6, WHITE); | |
| 151 | + | set_board_at(game->board, 5, 8, WHITE); | |
| 152 | + | set_board_at(game->board, 4, 7, WHITE); | |
| 153 | + | set_board_at(game->board, 6, 7, BLACK); | |
| 154 | + | ||
| 155 | + | set_board_at(game->board, 5, 2, BLACK); | |
| 156 | + | set_board_at(game->board, 5, 4, BLACK); | |
| 157 | + | set_board_at(game->board, 4, 3, BLACK); | |
| 158 | + | set_board_at(game->board, 6, 3, BLACK); | |
| 159 | + | ||
| 160 | + | go_score* score = score_game(game); | |
| 161 | + | print_board(game->board); | |
| 162 | + | printf("White groups: %ld\n", score->white_groups->length); | |
| 163 | + | printf("White points: %ld\n", score->white_points); | |
| 164 | + | printf("Black groups: %ld\n", score->black_groups->length); | |
| 165 | + | printf("Black points: %ld\n", score->black_points); | |
| 166 | + | ||
| 167 | + | ||
| 168 | + | delete_score(score); | |
| 169 | + | delete_game(game); | |
| 170 | + | puts("test5 finished"); | |
| 171 | + | } | |
| 172 | + | ||
| 141 | 173 | ||
| 142 | 174 | int main(int argc, char** argv) | |
| 143 | 175 | { | |
| 144 | - | game_state* game = create_game(19, 0); | |
| 176 | + | ||
| 145 | 177 | test1(); | |
| 146 | 178 | test2(); | |
| 147 | 179 | test3(); | |
| 148 | 180 | test4(); | |
| 149 | - | set_board_at(game->board, 2, 3, WHITE); | |
| 150 | - | char* ret = handle_gtp_cmd("showboard", game); | |
| 151 | - | printf("%s", ret); | |
| 152 | - | ||
| 153 | - | ||
| 154 | - | free(ret); | |
| 155 | - | delete_game(game); | |
| 181 | + | test5(); | |
| 156 | 182 | ||
| 157 | 183 | } | |