wm: libmarkus

Download patch

ref: 0cca37a43310c04d62de099d6337d276064848c3
author: bxh7 <nima@cloud9p.org>
date: Fri Aug 4 15:13:44 EDT 2023

uploaded source files

--- /dev/null
+++ b/README
@@ -1,0 +1,1 @@
+a library for writing Markus programs.
--- /dev/null
+++ b/libmarkus.c
@@ -1,0 +1,556 @@
+#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;
+}
--- /dev/null
+++ b/libmarkus.h
@@ -1,0 +1,122 @@
+/* this libmarkus only supports static M_MAX_BOARD_ROW and M_MAX_BOARD_COL */
+#ifndef __LIBMARKUS__H
+#define __LIBMARKUS__H
+
+#define M_MAX_BOARD_ROW 6
+#define M_MAX_BOARD_COL 6
+#define M_EMPTY_CELL ' '
+#include <stdint.h>
+#include <stdbool.h>
+#include <limits.h>
+
+
+typedef struct {
+	int free_pieces[2];
+	char player;
+	int direction;
+	int ecode;
+	char winner;
+	uint64_t ad_pieces;
+	uint64_t tag_pieces;
+} m_game_t;
+
+typedef struct
+{
+	int row;
+	int col;
+	// y = col, x = row
+} m_point_t;
+
+typedef struct
+{
+	m_point_t src;
+	m_point_t dst;
+} m_swap_t;
+
+enum m_ply_type {M_PLY_TYPE_MOVE, M_PLY_TYPE_PUT, M_PLY_TYPE_SWAP};
+typedef struct 
+{
+	enum m_ply_type ply_type;
+	m_swap_t ply_data;
+} m_ply_t;
+
+
+
+/* initializes a m_game_t, you should always call this function before any 
+ * other function on that m_game_t instance */
+void m_init_game(m_game_t* game);
+
+/* check if the game is over, return ' ' if it isn't, 
+ * and the winner's char (game->winner) if it is, it also
+ * set's game->winner. */
+char m_game_state(m_game_t* game);
+
+/* these functions are the three main operations of the game, 
+ * they also check if the game is over and refuse to do anything to 
+ * the game structure if it is(M_ECODE_GAMEOVER). */
+int m_put(m_game_t* game, m_point_t* point); 
+int m_move(m_game_t*  game, m_point_t* point);
+int m_swap(m_game_t* game, m_swap_t* swap);
+
+/* Parses "string" and passes it to m_swap/m_move/m_put 
+ * "string" should be null-terminated. string's format should
+ * be a single command charachter (m|p|s) followed by 
+ * its arguments, 'm' and 'p' require only one argument
+ * indicating the square that you wish to move/fill, and 
+ * 's' requires two arguments, a source and a destination square.
+ * squares are addressed as ROWCOL without any space in between,
+ * for example '45' represents the 4th row, 5th column. 
+ * examples:
+ * "m50"   => move the piece in 5th row, 0th column one square forward.
+ * "p00"   => put a free piece on the 0th row, 0th column, and decrement free_pieces[player] by 1.
+ * "s2040" => swap the piece on the 2nd row, 0th column with the piece on the 4th row, 0th column*/
+int m_play_notation(m_game_t* game, char* string);
+
+/* returns a null-terminated string describing game.ecode, every library function
+ * should and does set game.ecode.*/
+char* m_error_str(m_game_t* game);
+
+enum {
+	M_ECODE_OUTOFRANGE = INT_MIN,
+	M_ECODE_INVALIDINPUT,
+	M_ECODE_DSTNOTEMPTY,
+	M_ECODE_SRCNOTYOURS,
+	M_ECODE_NOTFIRSTROWS,
+	M_ECODE_NOTADJECENT,
+	M_ECODE_SRCORDSTEMPTY,
+	M_ECODE_SRCDSTSAMEPIECE,
+	M_ECODE_SRCDSTSAME,
+	M_ECODE_GAMEOVER,
+	M_ECODE_SUCCESS = 1
+};
+
+int m_is_move_legal(m_game_t* game, m_point_t* point);
+int m_is_swap_legal(m_game_t* game, m_swap_t* swap);
+int m_is_put_legal(m_game_t* game, m_point_t* point);
+int m_is_ply_legal(m_game_t* game, m_ply_t* p);
+//setters and getters
+int m_set_square(m_game_t*, int row, int col, char);
+char m_get_square(m_game_t*, int row, int col);
+
+char* m_get_board(m_game_t*); // returns a heap-allocated string representing the board.
+
+int m_get_free_pieces(m_game_t*, char player);
+int m_get_turn(m_game_t*);
+int m_game_is_over(m_game_t*);
+void m_ply_set_move(m_ply_t* p, int row, int col);
+void m_ply_set_put(m_ply_t* p, int row, int col);
+void m_ply_set_swap(m_ply_t*, int row1, int col1, int row2, int col2);
+
+m_swap_t m_ply_get_swap(m_ply_t* p);
+m_point_t m_ply_get_move(m_ply_t* p);
+m_point_t m_ply_get_put(m_ply_t* p);
+
+bool m_tag_wins(m_game_t* game); 
+bool m_ad_wins(m_game_t* game); 
+enum m_ply_type m_ply_get_type(m_ply_t* p);
+int m_undo_move(m_game_t*, m_point_t*);
+int m_undo_put(m_game_t*, m_point_t*);
+int m_undo_swap(m_game_t*, m_swap_t*);
+int m_do_ply(m_game_t* game, m_ply_t* ply);
+int m_undo_ply(m_game_t* game, m_ply_t* ply); 
+#endif