two-buckets: changes in move handling

This commit is contained in:
2021-09-08 12:57:42 +02:00
parent 7b92456167
commit 3a3d50b0a5

View File

@@ -3,17 +3,18 @@
#include <stdlib.h> #include <stdlib.h>
/* We will choose to use a board of (X,Y) squares, with the sizes of the 2 /* 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 * Current square (x,y) is enough to describe the status of the
* 2 buckets: b1 has x liters, b2 has y liters. * 2 buckets: b1 has x liters, b2 has y liters.
* At square (x, y), a maximum of 6 moves are possible : * At square (x, y), a maximum of 6 moves are possible :
* - (0, y) = empty b1 * - (0, y) = empty b1
* - (X, y) = fill b1 * - (X, y) = fill b1
* - (x, 0) = empty be * - (x, 0) = empty b2
* - (x, Y) = fill b2 * - (x, Y) = fill b2
* - (x - d, y + d) = b1 -> b2, with d = min(Y-y, x) * - (x - d, y + d) = b1 -> b2, with d = min(Y-y, x)
* - (x + d, y - d) = b2 -> b1, with d = min(X-x, y) * - (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. * same one.
* We will use BFS algorithm from initial square, which is (0, Y) or (X, 0). * 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. * We stop when x or y are equal to goal, or when no moves are possible.
@@ -26,28 +27,50 @@
#define AVAILABLE 0 #define AVAILABLE 0
#define FORBIDDEN (-1) #define FORBIDDEN (-1)
#define GOAL (-2) #define GOAL (-2)
//#define CURRENT (-4)
#define SQUARE(x, y, X) ((y) * (X) + (x)) /* from (col, row) to real array position (square), and vice-et-versa.
#define COL(p, X) ((p) % (X)) * X is column size.
#define ROW(p, X) ((p) / (X)) */
#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 /* 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. * 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)) #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); printf("board(%d, %d)\n", x, y);
for (int row = y-1; row >= 0; --row) { for (int row = y-1; row >= 0; --row) {
printf("%03d ", row); printf("%03d ", row);
for (int col = 0; col < x; ++col) { for (int col = 0; col < x; ++col) {
//printf("C: i=%d j=%d cell %d\n", i, j, i*x+j); //printf("C: i=%d j=%d cell %d\n", i, j, i*x+j);
//printf(" %d ", board[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)); //printf("row=%d col=%d x=%d square=%d\n", row, col, x, SQUARE(col, row, x));
} }
putchar('\n'); putchar('\n');
@@ -55,68 +78,74 @@ static void board_print(int *board, int x, int y)
putchar('\n'); 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); printf("board_init(%d, %d, goal=%d)\n", x, y, goal);
//board_print(board, x, y); //board_print(board, x, y);
for (i=0; i < x*y; ++i) { for (i=0; i < x*y; ++i) {
//printf("setting square %d (%d, %d)\n", i, COL(i, x), ROW(i, x)); printf("setting square %d (%d, %d)\n", i, COL(i, board), ROW(i, board));
*(board+i) = AVAILABLE; *(board->board+i) = AVAILABLE;
} }
board_print(board, x, y); board_print(board);
/* set target on goal row */ /* set target on goal row */
if (x >= goal) { if (x > goal) {
for (i = 0; i < y; ++i) { for (i = 0; i < y; ++i) {
printf("A: x=%d i=%d goal=%d setting %d\n", x, i, goal, SQUARE(goal, i, x)); printf("A: x=%d i=%d goal=%d setting %d\n", x, i, goal, SQUARE(goal, i, board));
board[SQUARE(goal, i, x)] = GOAL; board->board[SQUARE(goal, i, board)] = GOAL;
//board_print(board, x, y); //board_print(board, x, y);
} }
} }
/* set target on goal col */ /* set target on goal col */
board_print(board, x, y); board_print(board);
if (y >= goal) { if (y > goal) {
for (i = 0; i < x; ++i) { for (i = 0; i < x; ++i) {
printf("B: x=%d i=%d goal=%d setting %d\n", x, i, goal, SQUARE(i, goal, x)); printf("B: x=%d i=%d goal=%d setting %d\n", x, i, goal, SQUARE(i, goal, board));
board[SQUARE(i, goal, x)] = GOAL; board->board[SQUARE(i, goal, board)] = GOAL;
} }
board_print(board, x, y); board_print(board);
} }
} }
struct stack { static inline void enqueue(struct stack *s, int v)
int first;
int last;
int stack[];
};
static inline void push(struct stack *s, int v)
{ {
printf("pushing v=%d\n", v); printf("pushing v=%d\n", v);
//if (s->stack[s->last] == AVAILABLE) //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; int res;
printf("pop: first=%d last=%d val=%d\n", s->first, s->last, s->stack[s->first]); printf("dequeue: first=%d last=%d val=%d\n", s->first, s->last, s->s[s->first]);
res = s->first < s->last ? s->stack[s->first++] : -1; res = s->first < s->last ? s->s[s->first++] : -1;
printf("popping v=%d\n", res); printf("popping v=%d\n", res);
return 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 : ", printf("trying to [%s] on (%d, %d) - level=%d curval=%d : ",
text, col, row, level, board[SQUARE(col, row, X)]); text, col, row, level, board->board[SQUARE(col, row, board)]);
if (board[SQUARE(col, row, X)] == AVAILABLE) { if (board->board[SQUARE(col, row, board)] == AVAILABLE) {
printf("OK\n"); printf("OK\n");
board[SQUARE(col, row, X)] = level; move(board, stack, col, row, level, text);
push (stack, SQUARE(col, row, X)); //board->board[SQUARE(col, row, board)] = level;
} else if (board[SQUARE(col, row, X)] == GOAL) { //enqueue (stack, SQUARE(col, row, board));
} else if (board->board[SQUARE(col, row, board)] == GOAL) {
printf("**************** GOAL\n"); printf("**************** GOAL\n");
return 1; return 1;
} else { } else {
@@ -125,87 +154,68 @@ static int move_maybe(int *board, struct stack *stack, int col, int row, int lev
return 0; return 0;
} }
static bucket_result_t board_bfs(int *board, struct stack *stack, int start, static bucket_result_t board_bfs(struct board *board, struct stack *stack, int start,
const int X, const int Y, const int goal) const int goal)
{ {
int level = 0; int level = 0;
int cur_square; int cur_square;
int min, row, col, col1, row1; int min, row, col, col1, row1;
int X=board->X, Y=board->Y;
bucket_result_t res = { .possible = false, .move_count = 0 }; bucket_result_t res = { .possible = false, .move_count = 0 };
/* initialize 1st square used */ /* 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... */ /* now we consume as we can... */
while ((cur_square = pop(stack)) >= 0) { while ((cur_square = dequeue(stack)) >= 0) {
col=COL(cur_square, X); col=COL(cur_square, board);
row=ROW(cur_square, X); row=ROW(cur_square, board);
level=board[cur_square]; level=board->board[cur_square];
board_print(board, X, Y); board_print(board);
printf("move[pop=%d](col=%d, row=%d)=%d level=%d\n", printf("move[pop=%d](col=%d, row=%d)=%d level=%d\n",
cur_square, col, row, board[cur_square], level); cur_square, col, row, board->board[cur_square], level);
switch (level) { if (col > 0) {
case GOAL: /* we win */ col1 = 0;
printf("*** SHOULD NOT BE HERE\n"); 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; 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: found:
res.possible = true; res.possible = true;
res.move_count = level+1;//board[SQUARE(col1, row1, X)]+1; res.move_count = level+1;
if (col1 == goal) { if (col1 == goal) {
res.goal_bucket=BUCKET_ID_1; res.goal_bucket=BUCKET_ID_1;
res.other_bucket_liters=row1; res.other_bucket_liters=row1;
@@ -232,7 +242,8 @@ bucket_result_t measure(const bucket_liters_t b1,
.move_count = 0, .move_count = 0,
.other_bucket_liters = 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); printf("measure(b1=%d, b2=%d, goal=%d, start=%d)\n", b1, b2, goal, start);
if (goal > b1 && goal > b2) if (goal > b1 && goal > b2)
@@ -246,16 +257,41 @@ bucket_result_t measure(const bucket_liters_t b1,
return res; return res;
} }
struct stack *stack = malloc(sizeof(*stack) + if (!(stack = malloc(sizeof(*stack) + sizeof(*stack->s)*(b1+1)*(b2+1))))
sizeof(*stack->stack)*(b1+1)*(b1+1));
if (!stack)
return res; return res;
stack->first = 0; stack->first = 0;
stack->last = 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; return res;
} }
@@ -266,13 +302,14 @@ bucket_result_t measure(const bucket_liters_t b1,
int main(int ac, char **av) int main(int ac, char **av)
{ {
int arg=1; int arg=1;
int b1, b2, goal, start=0; int b1, b2, goal, start;
bucket_result_t res;; bucket_result_t res;;
for (; arg<ac-2; arg+=3) { for (; arg<ac-3; arg+=4) {
b1 = atoi(av[arg]); b1 = atoi(av[arg]);
b2 = atoi(av[arg+1]); b2 = atoi(av[arg+1]);
goal = atoi(av[arg+2]); goal = atoi(av[arg+2]);
start = atoi(av[arg+2]);
printf("b1=%d, b2=%d, goal=%d, start=%d\n", b1, b2, goal, start); printf("b1=%d, b2=%d, goal=%d, start=%d\n", b1, b2, goal, start);
res = measure(b1, b2, goal, start); res = measure(b1, b2, goal, start);
printf(" pos=%d count=%d goal=%d liters=%d\n", printf(" pos=%d count=%d goal=%d liters=%d\n",