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