ref: 0cca37a43310c04d62de099d6337d276064848c3
dir: /libmarkus.c/
#include "libmarkus.h" #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> /* used by main operations, m_move(), m_put(), m_swap() */ #define M_VERIFY_RANGE(ROW, COL) \ if (m_check_range(ROW, COL) < 0) { \ game->ecode = M_ECODE_OUTOFRANGE; \ return game->ecode; \ } #define BIT_INDEX(row, col) row *M_MAX_BOARD_COL + col // TODO static uint64_t common_winning_comb[] = { 0x041041041, 0x001041040, 0x082082082, 0x002082080, 0x104104104, 0x004104100, 0x208208208, 0x008208200, 0x410410410, 0x010410400, 0x820820820, 0x020820800, 0x042108420, 0x002108400, 0x001084210, 0x084210800, 0x810204081, 0x408102040, 0x020408102, }; static uint64_t ad_winning_combo[] = { 0x00000003f, 0x00000003e, 0x00000003c, 0x00000000f, 0x00000001f, 0x000000fc0, 0x000000f80, 0x000000f00, 0x0000007c0, 0x0000003c0, 0x00003f000, 0x00003e000, 0x00003c000, 0x00001f000, 0x00000f000, 0x001041041, 0x000041041, 0x002082082, 0x000082082, 0x004104104, 0x000104104, 0x008208208, 0x000208208, 0x010410410, 0x000410410, 0x020820820, 0x000820820, 0x042108400, 0x002108420, 0x002108400, 0x000108420, 0x004210800, 0x000084210, 0x810204080, 0x010204081, 0x000204081, 0x008102040, 0x000408102, }; static uint64_t tag_winning_combo[] = { 0xfc0000000, 0x7c0000000, 0x3c0000000, 0x780000000, 0xf80000000, 0x03f000000, 0x01f000000, 0x00f000000, 0x01e000000, 0x03e000000, 0x000fc0000, 0x0007c0000, 0x0003c0000, 0x000780000, 0x000f80000, 0x041041040, 0x041041000, 0x082082080, 0x082082000, 0x104104100, 0x104104000, 0x208208200, 0x208208000, 0x410410400, 0x410410000, 0x820820800, 0x820820000, 0x042108400, 0x002108420, 0x042108000, 0x002108400, 0x084210800, 0x004210800, 0x108420000, 0x810204080, 0x810204000, 0x408102000, 0x020408100, }; int m_check_range(int row, int col) { if (row < 0 || row >= M_MAX_BOARD_ROW || col < 0 || col >= M_MAX_BOARD_COL) return M_ECODE_OUTOFRANGE; return M_ECODE_SUCCESS; } int m_pindex(int row, int col) { return (row * M_MAX_BOARD_COL) + col; } void m_chplayer(m_game_t *game) { if (game->player == '@') game->player = '#'; else game->player = '@'; game->direction *= -1; } bool m_tag_wins(m_game_t *game) { for (int i = 0; i < sizeof(common_winning_comb) / sizeof(*common_winning_comb); i++) if ((game->tag_pieces & common_winning_comb[i]) == common_winning_comb[i]) return true; for (int i = 0; i < sizeof(tag_winning_combo) / sizeof(*tag_winning_combo); i++) if ((game->tag_pieces & tag_winning_combo[i]) == tag_winning_combo[i]) return true; return false; } bool m_ad_wins(m_game_t *game) { for (int i = 0; i < sizeof(common_winning_comb) / sizeof(common_winning_comb[0]); i++) if ((game->ad_pieces & common_winning_comb[i]) == common_winning_comb[i]) return true; for (int i = 0; i < sizeof(ad_winning_combo) / sizeof(*ad_winning_combo); i++) if ((game->ad_pieces & ad_winning_combo[i]) == ad_winning_combo[i]) return true; return false; } char m_game_state(m_game_t *game) { return m_ad_wins(game) ? '@' : m_tag_wins(game) ? '#' : ' '; } void _m_setupboard(m_game_t *game) { /* ___________________ |0 |1 |2 |3 |4 |5 | |__|__|__|__|__|__| |6 |7 |8 |9 |10|11| |__|__|__|__|__|__| |12|13|14|15|16|17| |__|__|__|__|__|__| |18|19|20|21|22|23| |__|__|__|__|__|__| |24|25|26|27|28|29| |__|__|__|__|__|__| |30|31|32|33|34|35| |__|__|__|__|__|__| therefore, the starting position for @ will be 111111 111111 000000 000000 000000 [ lower 36 bits ] and for # will be 000000 000000 000000 1111 1111 1111 [ lower 36 bits ] */ game->ad_pieces |= (uint64_t)0xFc0000000; game->tag_pieces |= (uint64_t)0x00000003F; } void m_init_game(m_game_t *game) { memset(game, 0, sizeof(m_game_t)); game->free_pieces[0] = (M_MAX_BOARD_ROW / 2 - 1) * M_MAX_BOARD_COL; game->free_pieces[1] = (M_MAX_BOARD_ROW / 2 - 1) * M_MAX_BOARD_COL; game->player = '@'; game->direction = -1; game->winner = ' '; game->ecode = M_ECODE_SUCCESS; game->tag_pieces = 0; game->ad_pieces = 0; _m_setupboard(game); } int m_is_put_legal(m_game_t *game, m_point_t *point) { M_VERIFY_RANGE(point->row, point->col) if (m_get_square(game, point->row, point->col) != M_EMPTY_CELL) { return M_ECODE_DSTNOTEMPTY; } if ((game->player == '@' && point->row < 4) || (game->player == '#' && point->row > 1)) { return M_ECODE_NOTFIRSTROWS; } return M_ECODE_SUCCESS; } int m_put(m_game_t *game, m_point_t *point) { if (game->winner != ' ') { return M_ECODE_GAMEOVER; } M_VERIFY_RANGE(point->row, point->col); int ecode = m_is_put_legal(game, point); if (ecode < 0) { game->ecode = ecode; return ecode; } m_set_square(game, point->row, point->col, game->player); int free_index = game->player == '@' ? 0 : 1; game->free_pieces[free_index]--; game->ecode = M_ECODE_SUCCESS; m_chplayer(game); //game->winner = m_game_state(game); return M_ECODE_SUCCESS; } int m_is_move_legal(m_game_t *game, m_point_t *point) { M_VERIFY_RANGE(point->row, point->col) if (m_get_square(game, point->row, point->col) != game->player) { return M_ECODE_SRCNOTYOURS; } if (m_get_square(game, point->row + game->direction, point->col) != ' ') { return M_ECODE_DSTNOTEMPTY; } return M_ECODE_SUCCESS; } int m_move(m_game_t *game, m_point_t *point) { if (game->winner != ' ') { return M_ECODE_GAMEOVER; } M_VERIFY_RANGE(point->row, point->col); int ecode = m_is_move_legal(game, point); if (ecode < 0) { game->ecode = ecode; return ecode; } m_set_square(game, point->row + game->direction, point->col, game->player); m_set_square(game, point->row, point->col, M_EMPTY_CELL); game->ecode = M_ECODE_SUCCESS; m_chplayer(game); /* change the player */ //game->winner = m_game_state(game); /* check to see if that move won the game */ return M_ECODE_SUCCESS; } int m_is_swap_legal(m_game_t *game, m_swap_t *swap) { m_point_t src = swap->src; m_point_t dst = swap->dst; M_VERIFY_RANGE(swap->src.row, swap->src.col) M_VERIFY_RANGE(swap->dst.row, swap->dst.col) if (swap->src.row - swap->dst.row > 1 || swap->src.row - swap->dst.row < -1 || swap->src.col - swap->dst.col > 1 || swap->src.col - swap->dst.col < -1) { return M_ECODE_NOTADJECENT; } if (m_get_square(game, src.row, src.col) == M_EMPTY_CELL || m_get_square(game, dst.row, dst.col) == M_EMPTY_CELL) { return M_ECODE_SRCORDSTEMPTY; } if (m_get_square(game, src.row, src.col) == m_get_square(game, dst.row, dst.col)) { return M_ECODE_SRCDSTSAMEPIECE; } if (src.row == dst.row && src.col == dst.col) { return M_ECODE_SRCDSTSAME; } return M_ECODE_SUCCESS; } int m_swap(m_game_t *game, m_swap_t *swap) { if (game->winner != ' ') { return M_ECODE_GAMEOVER; } M_VERIFY_RANGE(swap->src.row, swap->src.col); M_VERIFY_RANGE(swap->dst.row, swap->dst.col); int ecode = m_is_swap_legal(game, swap); if (ecode < 0) { game->ecode = ecode; return ecode; } m_point_t src = swap->src; m_point_t dst = swap->dst; char temp = m_get_square(game, src.row, src.col); // temp = src char dst_char = m_get_square(game, dst.row, dst.col); m_set_square(game, src.col, src.row, dst_char); // src = dst m_set_square(game, dst.row, dst.col, temp); // dst = temp game->ecode = M_ECODE_SUCCESS; m_chplayer(game); //game->winner = m_game_state(game); return M_ECODE_SUCCESS; } int m_play_notation(m_game_t *game, char *string) { /* parse "string" and dispatch it to the right function */ if (!string) { game->ecode = M_ECODE_INVALIDINPUT; return game->ecode; } if (strlen(string) < 3) { game->ecode = M_ECODE_INVALIDINPUT; return game->ecode; } m_point_t pos; /* for 'm' and 'p' */ m_swap_t swap; /* for 's' */ int retval = 0; switch (string[0]) { case 'm': case 'p': if (sscanf(&string[1], "%1d%1d", &pos.row, &pos.col) != 2) { game->ecode = M_ECODE_INVALIDINPUT; return game->ecode; } retval = string[0] == 'm' ? m_move(game, &pos) : m_put(game, &pos); break; case 's': if (sscanf(&string[1], "%1d%1d%1d%1d", &swap.src.row, &swap.src.col, &swap.dst.row, &swap.dst.col) != 4) { game->ecode = M_ECODE_INVALIDINPUT; return game->ecode; } retval = m_swap(game, &swap); break; default: game->ecode = M_ECODE_INVALIDINPUT; return game->ecode; } game->winner = m_game_state(game); // FIXME TODO, // this will be the main interface, m_move(), m_put(), m_swap() will be dumber, // they won't check for m_game_state(); return retval; } char *m_error_str(m_game_t *game) { switch (game->ecode) { case M_ECODE_OUTOFRANGE: return "Out of range"; break; case M_ECODE_INVALIDINPUT: return "Invalid input"; break; case M_ECODE_DSTNOTEMPTY: return "Destination is not empty"; break; case M_ECODE_SRCNOTYOURS: return "Source is not your piece"; break; case M_ECODE_NOTFIRSTROWS: return "Piece is not in your first two rows"; break; case M_ECODE_NOTADJECENT: return "Source and destination are not adjecent"; break; case M_ECODE_SRCORDSTEMPTY: return "Source or destination is empty"; break; case M_ECODE_SRCDSTSAMEPIECE: return "Source and destination have the same piece"; break; case M_ECODE_SRCDSTSAME: return "Source and destination are the same square"; break; case M_ECODE_GAMEOVER: return "Game is already over"; break; case M_ECODE_SUCCESS: return "Sucess"; break; } return "unknown error"; } char *m_get_board(m_game_t *g) { // 36 + 6, each cell takes 4 charachters to print + 6 newlines + null. char *buffer = (char *)malloc(42 * 11); assert(buffer); int buffer_index = 0; for (int row = 0; row < M_MAX_BOARD_ROW; row++) { for (int col = 0; col < M_MAX_BOARD_COL; col++) { buffer[buffer_index++] = ' '; buffer[buffer_index++] = m_get_square(g, row, col); buffer[buffer_index++] = ' '; buffer[buffer_index++] = '|'; } buffer[buffer_index++] = '\n'; } return buffer; } int m_set_square(m_game_t *game, int row, int col, char c) { if (c != ' ' && c != '@' && c != '#') return M_ECODE_INVALIDINPUT; if (row >= M_MAX_BOARD_ROW || col >= M_MAX_BOARD_COL) return M_ECODE_INVALIDINPUT; // 1 * 6 + (5-1) == 10 int bit_index = BIT_INDEX(row, col); assert(bit_index >= 0 && bit_index <= 35); uint64_t target_mask = ((uint64_t)1 << bit_index); game->ad_pieces &= ~target_mask; game->tag_pieces &= ~target_mask; if (c == '@') game->ad_pieces |= target_mask; else if (c == '#') game->tag_pieces |= target_mask; else if (c == ' ') ; // we'already cleared the specefied square. return M_ECODE_SUCCESS; } char m_get_square(m_game_t *game, int row, int col) { if (row < M_MAX_BOARD_ROW && col < M_MAX_BOARD_COL) { /* let's say the user asks for * 51. we give him 5*6 + 1 = 31 * 55. we give him 5*6 + 5 which is 35 * i think the indexes are all correct. * though i need to check.*/ int bit_index = BIT_INDEX(row, col); assert(bit_index >= 0 && bit_index <= 35); if (game->ad_pieces >> bit_index & 0x1) return '@'; else if (game->tag_pieces >> bit_index & 0x1) return '#'; return ' '; } game->ecode = M_ECODE_OUTOFRANGE; return '?'; } int m_get_free_pieces(m_game_t *g, char player) { switch (player) { case '@': return g->free_pieces[0]; // @ looks like a 0 case '#': return g->free_pieces[1]; default: return -1; } } int m_get_turn(m_game_t *g) { return g->player; } int m_game_is_over(m_game_t *g) { return g->winner == ' ' ? 0 : 1; } void m_ply_set_move(m_ply_t *p, int row, int col) { // assert(row < M_MAX_BOARD_ROW && col < M_MAX_BOARD_COL); p->ply_type = M_PLY_TYPE_MOVE; p->ply_data.src.row = row; p->ply_data.src.col = col; // we ignore dst for move and put } void m_ply_set_put(m_ply_t *p, int row, int col) { // assert(row < M_MAX_BOARD_ROW && col < M_MAX_BOARD_COL); p->ply_type = M_PLY_TYPE_PUT; p->ply_data.src.row = row; p->ply_data.src.col = col; // we ignore dst for move and put } void m_ply_set_swap(m_ply_t *p, int src_row, int src_col, int dst_row, int dst_col) { // assert(src_row < M_MAX_BOARD_ROW && src_col < M_MAX_BOARD_COL); // assert(dst_row < M_MAX_BOARD_ROW && dst_col < M_MAX_BOARD_COL); p->ply_type = M_PLY_TYPE_SWAP; p->ply_data.src.row = src_row; p->ply_data.src.col = src_col; p->ply_data.dst.row = dst_row; p->ply_data.dst.col = dst_col; } m_swap_t m_ply_get_swap(m_ply_t *p) { return p->ply_data; } m_point_t m_ply_get_move(m_ply_t *p) { return p->ply_data.src; } m_point_t m_ply_get_put(m_ply_t *p) { return p->ply_data.src; } enum m_ply_type m_ply_get_type(m_ply_t *p) { return p->ply_type; } int m_is_ply_legal(m_game_t *game, m_ply_t *p) { switch (m_ply_get_type(p)) { case M_PLY_TYPE_MOVE: return m_is_move_legal(game, &p->ply_data.src) == M_ECODE_SUCCESS ? 1 : 0; case M_PLY_TYPE_PUT: return m_is_put_legal(game, &p->ply_data.src) == M_ECODE_SUCCESS ? 1 : 0; break; case M_PLY_TYPE_SWAP: return m_is_swap_legal(game, &p->ply_data) == M_ECODE_SUCCESS ? 1 : 0; break; } } int m_undo_move(m_game_t* game, m_point_t* move) { M_VERIFY_RANGE(move->row, move->col); m_chplayer(game); // fix back the direction and game->player m_set_square(game, move->row + game->direction, move->col, ' '); m_set_square(game, move->row, move->col, game->player); game->winner = ' '; return M_ECODE_SUCCESS; } int m_undo_put(m_game_t* game, m_point_t* put) { M_VERIFY_RANGE(put->row, put->col); m_chplayer(game); m_set_square(game, put->row, put->col, ' '); game->winner = ' '; return M_ECODE_SUCCESS; } int m_undo_swap(m_game_t* game, m_swap_t* swp) { M_VERIFY_RANGE(swp->dst.row, swp->dst.col); M_VERIFY_RANGE(swp->src.row, swp->src.col); m_chplayer(game); game->winner = ' '; return m_swap(game, swp); // because i'm cool } int m_do_ply(m_game_t* game, m_ply_t* ply) { switch(m_ply_get_type(ply)){ case M_PLY_TYPE_MOVE: { m_point_t move = m_ply_get_move(ply); return m_move(game, &move); } break; case M_PLY_TYPE_PUT: { m_point_t put = m_ply_get_put(ply); return m_put(game, &put); } break; case M_PLY_TYPE_SWAP: { m_swap_t swp = m_ply_get_swap(ply); return m_swap(game, &swp); } break; } return M_ECODE_INVALIDINPUT; } int m_undo_ply(m_game_t* game, m_ply_t* ply) { switch(m_ply_get_type(ply)){ case M_PLY_TYPE_MOVE: { m_point_t move = m_ply_get_move(ply); m_undo_move(game, &move); } break; case M_PLY_TYPE_PUT: { m_point_t put = m_ply_get_put(ply); m_undo_put(game, &put); } break; case M_PLY_TYPE_SWAP: { m_swap_t swp = m_ply_get_swap(ply); m_undo_swap(game, &swp); } break; } return M_ECODE_INVALIDINPUT; }