From 3a3d50b0a5b0b00d06bb068574b996527ce0802c Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Wed, 8 Sep 2021 12:57:42 +0200 Subject: [PATCH] two-buckets: changes in move handling --- c/two-bucket/two_bucket.c | 275 +++++++++++++++++++++----------------- 1 file changed, 156 insertions(+), 119 deletions(-) diff --git a/c/two-bucket/two_bucket.c b/c/two-bucket/two_bucket.c index e2039f2..ae42071 100644 --- a/c/two-bucket/two_bucket.c +++ b/c/two-bucket/two_bucket.c @@ -3,17 +3,18 @@ #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. + * buckets (in fact +1, as buckets can have values 0...max), 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, 0) = empty b2 * - (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 + * 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. @@ -26,28 +27,50 @@ #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)) +/* from (col, row) to real array position (square), and vice-et-versa. + * X is column size. + */ +#define SQUARE(col, row, board) ((row) * (board->X) + (col)) +#define COL(square, board) ((square) % (board->X)) +#define ROW(square, board) ((square) / (board->X)) /* danger zone !! - this macro is BADâ„¢ in general, dangerous double - * evaluation, but ok here. OK, I promise, only here ;-) + * evaluation, but ok here. + * What did you ask ? Oh, yes, of course, I never use this macro, this is + * the first and last time ;-) * 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) +/* a queue for moves (BFS). I use a stupid max value, a linked list would + * be much better, would the buckets sizes being extremely huge. + */ +struct stack { + int first; + int last; + int s[]; +}; + +/* our board: Warning, X/Y are from 0 to size, not C array size. + * Example, if cols=2 and rows + */ +struct board { + int X; + int Y; + int board[]; +}; + +static void board_print(struct board *board) { + int x=board->X, y=board->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(" %2d ", board->board[SQUARE(col, row, board)]); //printf("row=%d col=%d x=%d square=%d\n", row, col, x, SQUARE(col, row, x)); } putchar('\n'); @@ -55,68 +78,74 @@ static void board_print(int *board, int x, int y) putchar('\n'); } -static inline void board_init(int x, int y, int goal, int *board) +static inline void board_init(struct board *board, int goal) { - int i; + int i, x=board->X, y=board->Y; 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; + printf("setting square %d (%d, %d)\n", i, COL(i, board), ROW(i, board)); + *(board->board+i) = AVAILABLE; } - board_print(board, x, y); + board_print(board); /* set target on goal row */ - if (x >= goal) { + 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; + printf("A: x=%d i=%d goal=%d setting %d\n", x, i, goal, SQUARE(goal, i, board)); + board->board[SQUARE(goal, i, board)] = GOAL; //board_print(board, x, y); } } /* set target on goal col */ - board_print(board, x, y); - if (y >= goal) { + board_print(board); + 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; + printf("B: x=%d i=%d goal=%d setting %d\n", x, i, goal, SQUARE(i, goal, board)); + board->board[SQUARE(i, goal, board)] = GOAL; } - board_print(board, x, y); + board_print(board); } } -struct stack { - int first; - int last; - int stack[]; -}; - -static inline void push(struct stack *s, int v) +static inline void enqueue(struct stack *s, int v) { printf("pushing v=%d\n", v); //if (s->stack[s->last] == AVAILABLE) - s->stack[s->last++] = v; + s->s[s->last++] = v; } -static inline int pop(struct stack *s) +static inline int dequeue(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("dequeue: first=%d last=%d val=%d\n", s->first, s->last, s->s[s->first]); + res = s->first < s->last ? s->s[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) +static int move(struct board *board, struct stack *stack, int col, int row, + int level, char *text) +{ + printf("move to [%s] on (%d, %d) - level=%d curval=%d : ", + text, col, row, level, board->board[SQUARE(col, row, board)]); + board->board[SQUARE(col, row, board)] = level; + enqueue (stack, SQUARE(col, row, board)); + return 0; +} + +static int move_maybe(struct board *board, struct stack *stack, int col, int row, + int level, 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) { + text, col, row, level, board->board[SQUARE(col, row, board)]); + if (board->board[SQUARE(col, row, board)] == AVAILABLE) { printf("OK\n"); - board[SQUARE(col, row, X)] = level; - push (stack, SQUARE(col, row, X)); - } else if (board[SQUARE(col, row, X)] == GOAL) { + move(board, stack, col, row, level, text); + //board->board[SQUARE(col, row, board)] = level; + //enqueue (stack, SQUARE(col, row, board)); + } else if (board->board[SQUARE(col, row, board)] == GOAL) { printf("**************** GOAL\n"); return 1; } else { @@ -125,87 +154,68 @@ static int move_maybe(int *board, struct stack *stack, int col, int row, int lev return 0; } -static bucket_result_t board_bfs(int *board, struct stack *stack, int start, - const int X, const int Y, const int goal) +static bucket_result_t board_bfs(struct board *board, struct stack *stack, int start, + const int goal) { int level = 0; int cur_square; int min, row, col, col1, row1; + int X=board->X, Y=board->Y; 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); + printf("bfs(start=%d, X=%d, Y=%d)\n", start, board->X, board->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); + while ((cur_square = dequeue(stack)) >= 0) { + col=COL(cur_square, board); + row=ROW(cur_square, board); + level=board->board[cur_square]; + board_print(board); 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"); + cur_square, col, row, board->board[cur_square], level); + if (col > 0) { + col1 = 0; + row1 = row; + if (move_maybe(board, stack, col1, row1, level+1, "empty b1")) + goto found; + } + if (row > 0) { + col1 = col; + row1 = 0; + if (move_maybe(board, stack, col1, row1, level+1, "empty b2")) + goto found; + } + if (col < X-1) { + col1 = X-1; + row1 = row; + if (move_maybe(board, stack, col1, row1, level+1, "fill b1")) + goto found; + } + if (row < Y-1) { + col1 = col; + row1 = Y-1; + if (move_maybe(board, stack, col1, row1, level+1, "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, "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, "pour b2 in b1")) 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; + goto end; /* no more possible move */ found: res.possible = true; - res.move_count = level+1;//board[SQUARE(col1, row1, X)]+1; + res.move_count = level+1; if (col1 == goal) { res.goal_bucket=BUCKET_ID_1; res.other_bucket_liters=row1; @@ -232,7 +242,8 @@ bucket_result_t measure(const bucket_liters_t b1, .move_count = 0, .other_bucket_liters = 0 }; - int board[(b1+1)*(b2+1)]; + struct board *board; + struct stack *stack; printf("measure(b1=%d, b2=%d, goal=%d, start=%d)\n", b1, b2, goal, start); if (goal > b1 && goal > b2) @@ -246,16 +257,41 @@ bucket_result_t measure(const bucket_liters_t b1, return res; } - struct stack *stack = malloc(sizeof(*stack) + - sizeof(*stack->stack)*(b1+1)*(b1+1)); - if (!stack) + if (!(stack = malloc(sizeof(*stack) + sizeof(*stack->s)*(b1+1)*(b2+1)))) return res; stack->first = 0; stack->last = 0; + printf("board size: %lu int=%lu n=%d, %lu\n", sizeof(*board), sizeof(int), + (b1+1)*(b2+1), + sizeof(*board) + sizeof(*board->board)*(b1+1)*(b2+1)); + if (!(board = malloc(sizeof(*board) + sizeof(*board->board)*(b1+1)*(b2+1)))) { + free(stack); + return res; + } + board->X = b1+1; + board->Y = b2+1; + board_init(board, goal); + /* only for exercism. better solutions could be found. Remove the next + * 3 lines to allow a different starting bucket (possibly better solution). + */ + board->board[SQUARE(0, 0, board)] = FORBIDDEN; + board->board[SQUARE(0, b2, board)] = FORBIDDEN; + board->board[SQUARE(b1, 0, board)] = FORBIDDEN; - board_init(b1+1, b2+1, goal, board); + if (start == BUCKET_ID_1) { + move(board, stack, b1, 0, 1, "start with b1"); + //enqueue(stack, SQUARE(b1, 0, board)); + //board->board[SQUARE(b1, 0, board)] = 1; + } else { + move(board, stack, 0, b2, 1, "start with b1"); + //enqueue(stack, SQUARE(0, b2, board)); + //board->board[SQUARE(0, b2, board)] = 1; + } + board_print(board); - res = board_bfs(board, stack, start, b1+1, b2+1, goal); + res = board_bfs(board, stack, start, goal); + free(stack); + free(board); return res; } @@ -266,13 +302,14 @@ bucket_result_t measure(const bucket_liters_t b1, int main(int ac, char **av) { int arg=1; - int b1, b2, goal, start=0; + int b1, b2, goal, start; bucket_result_t res;; - for (; arg