diff options
| -rw-r--r-- | include/tak.c | 59 | ||||
| -rw-r--r-- | include/tak.h | 15 | ||||
| -rw-r--r-- | src/ctaklm.c | 71 |
3 files changed, 81 insertions, 64 deletions
diff --git a/include/tak.c b/include/tak.c index e8bc846..d236b42 100644 --- a/include/tak.c +++ b/include/tak.c @@ -17,15 +17,16 @@ void reset_state(const uint8_t new_board_size) { if (new_board_size == 6) { board_size = 6; - white_flats = 30; black_flats = 30; + white_count = 128 | 30; + black_count = 128 | 30; } else { board_size = 5; - white_flats = 21; black_flats = 21; + white_count = 128 | 21; + black_count = 128 | 21; } - white_caps = 1; - black_caps = 1; ply = 0; + won = -1; current_colour = C_BLACK; for (uint8_t k = 0; k < NUM_SQUARES; k++ ) { @@ -51,6 +52,8 @@ enum E_RESULT try_place(const int8_t location, const enum COLOUR colour, const enum STONE_VARIANT stone) { + // Game is over? + if (won < -1) return GAME_END; // Can't place on an occupied square if (COUNT_AT(location)) { return ACT_ILLEGAL; @@ -59,10 +62,10 @@ try_place(const int8_t location, const enum COLOUR colour, case STONE_STANDING: if (ply < 2) return ACT_ILLEGAL; case STONE_FLAT: { if (colour == C_BLACK) { - if (black_flats) black_flats--; + if (black_count & 127) black_count--; else return ACT_ILLEGAL; } else { - if (white_flats) white_flats--; + if (white_count & 127) white_count--; else return ACT_ILLEGAL; } break; @@ -70,10 +73,10 @@ try_place(const int8_t location, const enum COLOUR colour, case STONE_CAPSTONE: { if (ply < 2) return ACT_ILLEGAL; if (colour == C_BLACK) { - if (black_caps) black_caps--; + if (black_count & 128) black_count &= 127; else return ACT_ILLEGAL; } else { - if (white_caps) white_caps--; + if (white_count & 128) white_count &= 127; else return ACT_ILLEGAL; } break; @@ -107,11 +110,12 @@ drop_stones(const int8_t location, const uint8_t count) { enum E_RESULT try_move(const int8_t location, const enum MOVE_DIRECTION direction, const uint8_t steps, const uint8_t drops[5]) { + // Game is over? + if (won < -1) return GAME_END; // Can't do this if (steps == 0 || steps > 5) return ACT_ILLEGAL; - - int8_t delta; // Is the desired direction and count on the board? + int8_t delta = 0; switch (direction) { case M_UP: { delta = +board_size; @@ -128,7 +132,6 @@ try_move(const int8_t location, const enum MOVE_DIRECTION direction, if (location + delta * steps > NUM_SQUARES) return ACT_ILLEGAL; break; }; - default: // Why do i need this to satisfy GCC? case M_LEFT: { delta = -1; if (location + delta * steps < 0) return ACT_ILLEGAL; @@ -166,7 +169,8 @@ try_move(const int8_t location, const enum MOVE_DIRECTION direction, if ( (total > board_size) || (total > COUNT_AT(location)) ) return ACT_ILLEGAL; - // Nothing illegal, do it + // Nothing illegal, do it. First we add the stones to the + // destination squares uint8_t j = total; for (uint8_t k = 0; k < steps; k++) { j -= drops[k]; @@ -175,7 +179,7 @@ try_move(const int8_t location, const enum MOVE_DIRECTION direction, (colours[location] >> j) & (0xFFFF >> (0x10 - drops[k])), (k == steps - 1) ? STONE_AT(location) : STONE_FLAT); } - + // Then we drop them from the source drop_stones(location, total); return ACT_OK; @@ -192,7 +196,9 @@ board_full(void) { return 1; } -// direction == 0 --> left-to-right, direction == 1 --> bottom-to-top +// Depth-first search of the board for a road with a given +// directionality: +// direction = 0 --> left-to-right, direction = 1 --> bottom-to-top static uint8_t dfs_road(uint8_t dfs_stack[NUM_SQUARES], uint8_t dfs_pntr, const enum COLOUR colour, const uint8_t direction) { @@ -207,8 +213,8 @@ dfs_road(uint8_t dfs_stack[NUM_SQUARES], uint8_t dfs_pntr, // are inhabited, and are of the appropriate colour // Direction: > (same row) - if ( (cur + 1 < NUM_SQUARES) && ((cur + 1) % board_size > 0) - && (COUNT_AT(cur + 1)) + if ( ((cur % board_size) + 1 < board_size) + && (COUNT_AT(cur + 1)) // wont ever be out of bounds && ((colours[cur+1] & 1) == colour) && ((celldat[cur+1] & DFS_MASK) == 0) ) { dfs_stack[dfs_pntr++] = cur + 1; @@ -217,7 +223,7 @@ dfs_road(uint8_t dfs_stack[NUM_SQUARES], uint8_t dfs_pntr, // Direction: < (same row) if ( (cur % board_size > 0) - && (COUNT_AT(cur - 1)) + && (COUNT_AT(cur - 1)) // wont ever be out of bounds && ((colours[cur-1] & 1) == colour) && ((celldat[cur-1] & DFS_MASK) == 0) ) { dfs_stack[dfs_pntr++] = cur - 1; @@ -245,7 +251,7 @@ dfs_road(uint8_t dfs_stack[NUM_SQUARES], uint8_t dfs_pntr, return 0; } -static enum E_RESULT +static enum WIN_TYPE check_road_colour(const enum COLOUR colour) { uint8_t dfs_stack[NUM_SQUARES]; uint8_t dfs_pntr; @@ -253,7 +259,7 @@ check_road_colour(const enum COLOUR colour) { // Prime the depth-first-search stack with all boundary cells of // colour COLOUR. - const enum E_RESULT winner = + const enum WIN_TYPE winner = (colour == C_BLACK) ? WIN_ROAD_BLACK : WIN_ROAD_WHITE; // We're using the two left-over bits in data_t to track whether @@ -288,13 +294,13 @@ check_road_colour(const enum COLOUR colour) { } if (dfs_road(dfs_stack, dfs_pntr, colour, 1)) return winner; - return ACT_OK; + return -1; } -enum E_RESULT +enum WIN_TYPE check_win(void) { // Do we do a flat count? - if (black_flats == 0 || white_flats == 0 || board_full()) { + if ((black_count & 127) == 0 || (white_count & 127) == 0 || board_full()) { int8_t total = 0; for (uint8_t k = 0; k < NUM_SQUARES; k++) { if (STONE_AT(k) == STONE_FLAT) { @@ -312,7 +318,7 @@ check_win(void) { // Road? - enum E_RESULT rb, rw; + enum WIN_TYPE rb, rw; rb = check_road_colour(C_BLACK); rw = check_road_colour(C_WHITE); @@ -498,6 +504,8 @@ is_not_placement(char *ptn) { enum E_RESULT do_ptn(char *ptn) { + if (won < -1) return GAME_END; + enum E_RESULT res; uint8_t location; @@ -520,7 +528,10 @@ do_ptn(char *ptn) { if (res == ACT_OK) { next_ply(); // Could be less conservative here :) - if (ply > 2) return check_win(); + if (ply > board_size) { + won = check_win(); + if (won < -1) return GAME_END; + } } return res; diff --git a/include/tak.h b/include/tak.h index 7d96b9a..f6cde6a 100644 --- a/include/tak.h +++ b/include/tak.h @@ -1,10 +1,7 @@ #include <stdint.h> -enum E_RESULT { - ACT_OK, ACT_ILLEGAL, ACT_OVERFLOW, - PTN_VALID, PTN_INVALID, - WIN_DRAW, WIN_FLAT_WHITE, WIN_FLAT_BLACK, - WIN_ROAD_WHITE, WIN_ROAD_BLACK, WIN_DRAGON }; +enum E_RESULT {ACT_OK, ACT_ILLEGAL, ACT_OVERFLOW, + PTN_VALID, PTN_INVALID, GAME_END }; // ------------------------------------------------------------------- // State management @@ -12,6 +9,9 @@ enum E_RESULT { enum COLOUR { C_WHITE, C_BLACK }; enum STONE_VARIANT { STONE_FLAT, STONE_STANDING, STONE_CAPSTONE }; enum MOVE_DIRECTION { M_UP, M_DOWN, M_LEFT, M_RIGHT }; +enum WIN_TYPE { WIN_DRAW, + WIN_FLAT_WHITE, WIN_FLAT_BLACK, + WIN_ROAD_WHITE, WIN_ROAD_BLACK, WIN_DRAGON}; typedef uint8_t data_t; typedef uint16_t colour_stack_t; @@ -22,11 +22,12 @@ typedef uint16_t colour_stack_t; #define COUNT_AT(l) (celldat[(l)] >> NUM_SHIFT) #define THE_COORDS(col,row) ((col)+(row)*board_size) +enum WIN_TYPE won; uint8_t board_size; data_t celldat[36]; colour_stack_t colours[36]; enum COLOUR current_colour; -uint8_t white_flats, black_flats, white_caps, black_caps, ply; +uint8_t white_count, black_count, ply; void reset_state(const uint8_t new_board_size); void next_ply(void); @@ -39,7 +40,7 @@ enum E_RESULT try_move(const int8_t location, const enum MOVE_DIRECTION direction, const uint8_t steps, const uint8_t drops[5]); -enum E_RESULT +enum WIN_TYPE check_win(void); // ------------------------------------------------------------------- diff --git a/src/ctaklm.c b/src/ctaklm.c index 5dbfde8..5ad7e8e 100644 --- a/src/ctaklm.c +++ b/src/ctaklm.c @@ -146,15 +146,15 @@ print_info(void) { rst, (ply < 2) ? " (counter-play start)" : ""); printf("Flats remaining: %sWhite%s %02d, %sBlack%s %02d\n", - wht,rst,white_flats, - blk,rst,black_flats); + wht,rst,white_count & 127, + blk,rst,black_count & 127); printf("Caps remaining: %sWhite%s %d, %sBlack%s %d\n", - wht,rst,white_caps, - blk,rst,black_caps); + wht,rst,white_count >> 7, + blk,rst,black_count >> 7); } char *gamelog = 0; -uint8_t auto_board = 0xFF, auto_info = 0xFF, won = 0x00; +uint8_t auto_board = 0xFF, auto_info = 0xFF; static void append_to_gamelog(char *line, const uint8_t win_line) { @@ -182,43 +182,49 @@ append_to_gamelog(char *line, const uint8_t win_line) { static void end_game(char *line, char *win) { - won = 0xFF; append_to_gamelog(line, 0); append_to_gamelog(win, 1); print_board(); puts("Game over:"); puts(gamelog); - puts("\nEnter `new' to play again."); + putchar('\n'); } static void handle_turn(char *line) { - if (won) { - puts("Game over, enter `new' to play again."); - } else { - switch (do_ptn(line)) { - // Errors - case PTN_INVALID : { puts("Invalid PTN."); break; } - case ACT_ILLEGAL : { puts("Illegal action."); break; } - case ACT_OVERFLOW : { - puts("Move would cause internal overflow, select another."); - break; - } - // Valid, append to game log - case PTN_VALID: - case ACT_OK: { - append_to_gamelog(line, 0); - if (auto_info) print_info(); - if (auto_board) print_board(); - break; + // Track win state + uint8_t new_win = (won == -1); + switch (do_ptn(line)) { + // Errors + case PTN_INVALID: { puts("Invalid PTN."); break; } + case ACT_ILLEGAL: { puts("Illegal action."); break; } + case ACT_OVERFLOW: { + puts("Move would cause internal overflow, select another."); + break; + } + // Game has ended + case GAME_END: { + // Did it end this turn? + if (new_win) { + switch (won) { + case WIN_DRAGON: { end_game(line,"R-R"); break; } + case WIN_DRAW: { end_game(line,"1/2-1/2"); break; } + case WIN_FLAT_BLACK: { end_game(line,"0-F"); break; } + case WIN_FLAT_WHITE: { end_game(line,"F-0"); break; } + case WIN_ROAD_BLACK: { end_game(line,"0-R"); break; } + case WIN_ROAD_WHITE: { end_game(line,"R-0"); break; } + } } - // A win - case WIN_DRAGON: { end_game(line,"R-R"); break; } - case WIN_DRAW: { end_game(line,"1/2-1/2"); break; } - case WIN_FLAT_BLACK: { end_game(line,"0-F"); break; } - case WIN_FLAT_WHITE: { end_game(line,"F-0"); break; } - case WIN_ROAD_BLACK: { end_game(line,"0-R"); break; } - case WIN_ROAD_WHITE: { end_game(line,"R-0"); break; } + puts("Game over, enter `new' to play again."); + break; + } + // Valid, append to game log + case PTN_VALID: + case ACT_OK: { + append_to_gamelog(line, 0); + if (auto_info) print_info(); + if (auto_board) print_board(); + break; } } } @@ -226,7 +232,6 @@ handle_turn(char *line) { static void new_game(uint8_t size) { reset_state(size); - won = 0x00; printf("New %dx%d game!\n",size,size); if (gamelog) gamelog = realloc(gamelog, sizeof(char)); else gamelog = malloc(sizeof(char)); |
