#include "two_bucket.h" #include #include /* We will choose to use a board of (X,Y) squares, with the sizes of the 2 * buckets, where we move the current point at each step. * Current square (x,y) is enough to describe the status of the * 2 buckets: b1 has x liters, b2 has y liters. * At square (x, y), a maximum of 6 moves are possible : * - (0, y) = empty b1 * - (X, y) = fill b1 * - (x, 0) = empty be * - (x, Y) = fill b2 * - (x - d, y + d) = b1 -> b2, with d = min(Y-y, x) * - (x + d, y - d) = b2 -> b1, with d = min(X-x, y) * The board keeps tracks of visited squares. We cannot pass twice the * same one. * We will use BFS algorithm from initial square, which is (0, Y) or (X, 0). * We stop when x or y are equal to goal, or when no moves are possible. * */ /* The board contains a positive integer for its current move, otherwise the * following values */ #define AVAILABLE 0 #define FORBIDDEN (-1) #define GOAL (-2) //#define CURRENT (-4) #define SQUARE(x, y, X) ((y) * (X) + (x)) #define COL(p, X) ((p) % (X)) #define ROW(p, X) ((p) / (X)) /* danger zone !! - this macro is BADâ„¢ in general, dangerous double * evaluation, but ok here. OK, I promise, only here ;-) * If unsure why this is very badâ„¢, let me a comment on exercism. */ #define MAX(x, y) ((x) >= (y)? (x): (y)) #define MIN(x, y) ((x) <= (y)? (x): (y)) static void board_print(int *board, int x, int y) { printf("board(%d, %d)\n", x, y); for (int row = y-1; row >= 0; --row) { printf("%03d ", row); for (int col = 0; col < x; ++col) { //printf("C: i=%d j=%d cell %d\n", i, j, i*x+j); //printf(" %d ", board[i*x+j]); printf(" %2d ", board[SQUARE(col, row, x)]); //printf("row=%d col=%d x=%d square=%d\n", row, col, x, SQUARE(col, row, x)); } putchar('\n'); } putchar('\n'); } static inline void board_init(int x, int y, int goal, int *board) { int i; printf("board_init(%d, %d, goal=%d)\n", x, y, goal); //board_print(board, x, y); for (i=0; i < x*y; ++i) { //printf("setting square %d (%d, %d)\n", i, COL(i, x), ROW(i, x)); *(board+i) = AVAILABLE; } board_print(board, x, y); /* set target on goal row */ if (x >= goal) { for (i = 0; i < y; ++i) { printf("A: x=%d i=%d goal=%d setting %d\n", x, i, goal, SQUARE(goal, i, x)); board[SQUARE(goal, i, x)] = GOAL; //board_print(board, x, y); } } /* set target on goal col */ board_print(board, x, y); if (y >= goal) { for (i = 0; i < x; ++i) { printf("B: x=%d i=%d goal=%d setting %d\n", x, i, goal, SQUARE(i, goal, x)); board[SQUARE(i, goal, x)] = GOAL; } board_print(board, x, y); } } struct stack { int first; int last; int stack[]; }; static inline void push(struct stack *s, int v) { printf("pushing v=%d\n", v); //if (s->stack[s->last] == AVAILABLE) s->stack[s->last++] = v; } static inline int pop(struct stack *s) { int res; printf("pop: first=%d last=%d val=%d\n", s->first, s->last, s->stack[s->first]); res = s->first < s->last ? s->stack[s->first++] : -1; printf("popping v=%d\n", res); return res; } static int move_maybe(int *board, struct stack *stack, int col, int row, int level ,int X, char *text) { printf("trying to [%s] on (%d, %d) - level=%d curval=%d : ", text, col, row, level, board[SQUARE(col, row, X)]); if (board[SQUARE(col, row, X)] == AVAILABLE) { printf("OK\n"); board[SQUARE(col, row, X)] = level; push (stack, SQUARE(col, row, X)); } else if (board[SQUARE(col, row, X)] == GOAL) { printf("**************** GOAL\n"); return 1; } else { printf("Impossible\n"); } return 0; } static bucket_result_t board_bfs(int *board, struct stack *stack, int start, const int X, const int Y, const int goal) { int level = 0; int cur_square; int min, row, col, col1, row1; bucket_result_t res = { .possible = false, .move_count = 0 }; /* initialize 1st square used */ printf("bfs(start=%d, X=%d, Y=%d)\n", start, X, Y); if (start == BUCKET_ID_1) { push(stack, SQUARE(X-1, 0, X)); board[SQUARE(X-1, 0, X)] = 1; board[SQUARE(0, Y-1, X)] = FORBIDDEN; } else { push(stack, SQUARE(0, Y-1, X)); board[SQUARE(0, Y-1, X)] = 1; board[SQUARE(X-1, 0, X)] = FORBIDDEN; } board_print(board, X, Y); /* now we consume as we can... */ while ((cur_square = pop(stack)) >= 0) { col=COL(cur_square, X); row=ROW(cur_square, X); level=board[cur_square]; board_print(board, X, Y); printf("move[pop=%d](col=%d, row=%d)=%d level=%d\n", cur_square, col, row, board[cur_square], level); switch (level) { case GOAL: /* we win */ printf("*** SHOULD NOT BE HERE\n"); goto found; case AVAILABLE: /* should not happen */ goto end; default: if (col > 0) { col1 = 0; row1 = row; if (move_maybe(board, stack, col1, row1, level+1, X, "empty b1")) goto found; } if (row > 0) { col1 = col; row1 = 0; if (move_maybe(board, stack, col1, row1, level+1, X, "empty b2")) goto found; } if (col < X-1) { col1 = X-1; row1 = row; if (move_maybe(board, stack, col1, row1, level+1, X, "fill b1")) goto found; } if (row < Y-1) { col1 = col; row1 = Y-1; if (move_maybe(board, stack, col1, row1, level+1, X, "fill b2")) goto found; } if (col > 0 && row < Y-1) { min = MIN(col, Y-row-1); col1 = col-min; row1 = row+min; if (move_maybe(board, stack, col1, row1, level+1, X, "pour b1 in b2")) goto found; } if (row > 0 && col < X-1) { min = MIN(row, X-col-1); col1 = col+min; row1 = row-min; if (move_maybe(board, stack, col1, row1, level+1, X, "pour b2 in b1")) goto found; } } } goto end; found: res.possible = true; res.move_count = level+1;//board[SQUARE(col1, row1, X)]+1; if (col1 == goal) { res.goal_bucket=BUCKET_ID_1; res.other_bucket_liters=row1; } else { res.goal_bucket=BUCKET_ID_2; res.other_bucket_liters=col1; } end: printf("END: possible=%d\n", res.possible); printf(" move-count=%d\n", res.move_count); printf(" goal-bucket=%d\n", res.goal_bucket); printf(" other-bucket=%d\n", res.other_bucket_liters); return res; } bucket_result_t measure(const bucket_liters_t b1, const bucket_liters_t b2, const bucket_liters_t goal, const bucket_id_t start) { bucket_result_t res = { .possible = false, .move_count = 0, .other_bucket_liters = 0 }; int board[(b1+1)*(b2+1)]; printf("measure(b1=%d, b2=%d, goal=%d, start=%d)\n", b1, b2, goal, start); if (goal > b1 && goal > b2) return res; if ((start == BUCKET_ID_1 && goal == b1) || (start == BUCKET_ID_2 && goal == b2)) { printf("done in 1\n"); res.possible = true; res.move_count = 1; res.goal_bucket = start; return res; } struct stack *stack = malloc(sizeof(*stack) + sizeof(*stack->stack)*(b1+1)*(b1+1)); if (!stack) return res; stack->first = 0; stack->last = 0; board_init(b1+1, b2+1, goal, board); res = board_bfs(board, stack, start, b1+1, b2+1, goal); return res; } /* See GNUmakefile below for explanation * https://github.com/braoult/exercism/blob/master/c/templates/GNUmakefile */ #ifdef UNIT_TEST int main(int ac, char **av) { int arg=1; int b1, b2, goal, start=0; bucket_result_t res;; for (; arg