wm: libmarkus

ref: 0cca37a43310c04d62de099d6337d276064848c3
dir: /libmarkus.c/

View raw version
#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;
}