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;
}