Lock all cells in the end so sorters finish pending operations.
[funnysort.git] / funnysort.c
blobf9e2ddd1b1bc20ada8c1b0f0284c6ecbdc3d6ef4
1 /* funnysort.c */
3 /* remove the next line to enable asserts */
4 #define NDEBUG
6 #include <stdio.h>
7 #include <assert.h>
8 #include <stdlib.h>
9 #include <pthread.h>
10 #include <unistd.h>
11 #include <stdbool.h>
12 #include <signal.h>
14 #include "congl.h"
16 #define WIDTH 50
17 #define HEIGHT 30
18 #define NCYCLES 1000000
19 #define SLEEP_TIME 0
20 #define SORTER_MOVE_TRIES 100
21 #define FREE_PER_ITEM 50
22 #define NSORTERS 5
24 typedef enum {A=0, EMPTY, SORTER} item_t;
26 struct board_s {
27 unsigned int width;
28 unsigned int height;
29 bool size_valid;
30 item_t **surface;
31 pthread_mutex_t **lock;
32 bool surface_valid;
34 typedef struct board_s board_t;
36 struct sorter_s {
37 unsigned int column;
38 unsigned int line;
40 typedef struct sorter_s sorter_t;
42 void count_item (board_t *, item_t *, unsigned int *);
43 void drop_item (unsigned int, unsigned int, board_t *, item_t *);
44 void erase_board (board_t *);
45 void fetch_item(unsigned int, unsigned int, item_t *);
46 void finish_board(board_t *);
47 void move_sorter (board_t *, sorter_t *);
48 void peek_item (unsigned int, unsigned int, board_t *, item_t *);
49 void pick_item (unsigned int, unsigned int, board_t *, item_t *);
50 void poke_item (unsigned int, unsigned int, board_t *, item_t);
51 void print_board (board_t *);
52 void print_item (unsigned int, unsigned int, item_t);
53 void print_mutexes (board_t *board);
54 void *launch_sorter (void *);
55 void setup_board(board_t *);
57 /* Erase the output medium. */
58 void erase_board (board_t *board)
60 conglReset();
61 conglClearScreen();
62 conglMoveCursor(1, 1);
65 void move_sorter(board_t *board, sorter_t *sorter)
67 /* sorter current position was locked before calling this function */
69 assert(sorter != NULL);
70 assert(sorter->column < WIDTH);
71 assert(sorter->line < HEIGHT);
73 int h, v;
74 unsigned int newline, newcolumn;
75 unsigned int ncycles;
76 item_t item, sorter_item;
78 ncycles = 0;
79 do {
80 /* Select random displacement. */
81 h = random()%3 - 1;
82 v = random()%3 - 1;
84 newline = (sorter->line + (h+HEIGHT)) % HEIGHT;
85 newcolumn = (sorter->column + (v+WIDTH)) % WIDTH;
87 /* Do not try to move to current position */
88 if ((newline != sorter->line) && (newcolumn != sorter->column)) {
89 /* lock position to where the sorter may move */
90 pthread_mutex_lock((board->lock)[newline]+newcolumn);
91 peek_item(newline, newcolumn, board, &item);
92 if (item != EMPTY) {
93 /* unlock if position if not available */
94 pthread_mutex_unlock((board->lock)[newline]+newcolumn);
97 ++ncycles;
98 } while ((item != EMPTY) && (ncycles < SORTER_MOVE_TRIES));
100 /* If sorter failed to move, respawn in random position */
101 while (item != EMPTY) {
102 newline = random()%HEIGHT;
103 newcolumn = random()%WIDTH;
105 /* Do not try to move to current position */
106 if ((newline != sorter->line) && (newcolumn != sorter->column)) {
107 /* lock position to where the sorter may move */
108 pthread_mutex_lock((board->lock)[newline]+newcolumn);
109 peek_item(newline, newcolumn, board, &item);
110 if (item != EMPTY) {
111 /* unlock if position if not available */
112 pthread_mutex_unlock((board->lock)[newline]+newcolumn);
117 /* Update board. */
119 /* Don't reset the item if sorter just droped one. */
120 /* If an item was droped, it will be different from SORTER. */
121 peek_item(sorter->line, sorter->column, board, &sorter_item);
122 if (sorter_item == SORTER) {
123 poke_item (sorter->line, sorter->column, board, EMPTY);
125 poke_item(newline, newcolumn, board, SORTER);
126 sorter->line = newline;
127 sorter->column = newcolumn;
129 pthread_mutex_unlock((board->lock)[newline]+newcolumn);
131 assert(sorter->column < WIDTH);
132 assert(sorter->line < HEIGHT);
135 void *launch_sorter (void *board)
137 int itera;
138 item_t item;
139 unsigned int line;
140 unsigned int column;
141 sorter_t sorter;
143 /* Place sorter in some free position. */
144 do {
145 line = random()%HEIGHT;
146 column = random()%WIDTH;
147 peek_item(line, column, board, &item);
148 } while (item != EMPTY);
149 poke_item(line, column, board, SORTER);
150 sorter.line = line;
151 sorter.column = column;
153 for (itera = 0; itera <= NCYCLES; ++itera) {
154 //for (itera = 0; 0==0; ++itera) {
155 do {
156 move_sorter(board, &sorter);
157 pick_item(sorter.line, sorter.column, board, &item);
159 usleep(SLEEP_TIME);
160 } while (item == EMPTY);
162 /* because the sorter allways moves, if it drops the item it will not
163 * remain on the top of it */
164 while (item != EMPTY) {
165 line = sorter.line;
166 column = sorter.column;
168 // Because the sorter may drop an item, it's current position must
169 // be locked until after it moves.
170 pthread_mutex_lock((((board_t *)board)->lock)[line]+column);
171 drop_item(sorter.line, sorter.column, board, &item);
172 move_sorter(board, &sorter);
173 pthread_mutex_unlock((((board_t *)board)->lock)[line]+column);
175 usleep(SLEEP_TIME);
178 /* leave the premisses */
179 //for (r = 0; (r < 10); ++r) {
180 // move(board, sorter);
184 return NULL;
187 /* Try to pick an item. Don't pick other sorters :) */
188 void pick_item (unsigned int line, unsigned int column, board_t *board, item_t *item)
190 assert (board != NULL);
191 assert (board->size_valid);
192 assert (board->surface_valid);
193 assert (line < board->height);
194 assert (column < board->width);
195 assert (item != NULL);
196 assert (*item == EMPTY);
198 int newline, newcolumn;
199 item_t newitem;
200 int h, v; /* h: horizontal; v: vertical */
202 for (h = -1; (h <= 1) && (*item == EMPTY); ++h) {
203 for (v = -1; (v <= 1) && (*item == EMPTY); ++v) {
204 /* newline and newcolumn need to be allawys positive */
205 newline = (line+(h+HEIGHT))%HEIGHT;
206 newcolumn = (column+(v+WIDTH))%WIDTH;
208 /* Don't check current position because it is already locked */
209 if ((h != 0) && (v != 0)) {
210 pthread_mutex_lock((board->lock)[newline]+newcolumn);
211 peek_item(newline, newcolumn, board, &newitem);
212 if((newitem != EMPTY) && (newitem != SORTER)) {
213 *item = newitem;
214 poke_item(newline, newcolumn, board, EMPTY);
215 /* we have the item */
217 pthread_mutex_unlock((board->lock)[newline]+newcolumn);
222 /* we either pick an item or leave it as it is */
223 assert ((*item == newitem) || (*item == EMPTY));
224 assert (*item != SORTER);
227 /* Try to drop one item. The item needs to be droped in a position adjacent to
228 * a similar item.
230 void drop_item (unsigned int line, unsigned int column, board_t *board, item_t *item)
232 assert (board != NULL);
233 assert (board->size_valid);
234 assert (board->surface_valid);
235 assert (line < board->height);
236 assert (column < board->width);
237 assert (item != NULL);
238 assert (*item != EMPTY);
239 assert (*item != SORTER);
241 int newline, newcolumn;
242 int h, v; /* h: horizontal, v: vertical */
243 item_t newitem;
245 for (h = -1; (h <= 1) && (*item != EMPTY); ++h) {
246 for (v = -1; (v <= 1) && (*item != EMPTY); ++v) {
247 /* newline and newcolumn need to be allawys positive */
248 newline = (line+h+HEIGHT)%HEIGHT;
249 newcolumn = (column+v+WIDTH)%WIDTH;
251 /* Don't check current position because it is already locked */
252 if ((h != 0) && (v != 0)) {
253 pthread_mutex_lock((board->lock)[newline]+newcolumn);
254 peek_item(newline, newcolumn, board, &newitem);
255 if(newitem == *item) {
256 poke_item(line, column, board, newitem);
257 *item = EMPTY;
258 /* and the item is down */
260 pthread_mutex_unlock((board->lock)[newline]+newcolumn);
266 /* Print the base of the board, without any items. */
267 void print_board(board_t *board)
269 assert(board->size_valid);
270 assert(board->surface_valid);
272 conglClearScreen();
273 conglMoveCursor(1, 1);
276 void print_item(unsigned int line, unsigned int column, item_t item)
278 struct properties_s {
279 conglColor_t bg;
280 conglColor_t fg;
281 char symbol;
283 typedef struct properties_s properties_t;
285 properties_t properties[] = {
286 {congl_magenta, congl_white, ' '},
287 {congl_green, congl_white, ' '},
288 {congl_black, congl_white, ' '}
289 //{'i'}, {'f'}, {'s'}
292 properties_t p;
294 p = properties[item];
295 conglDrawCharFull(line+1, column+1, p.symbol, p.fg, p.bg);
297 //printf("%c: %d,%d\n", p.symbol, column, line);
300 /* Read an item from the board. */
301 void peek_item (unsigned int line, unsigned int column, board_t *board, item_t *item)
303 assert(board != NULL);
304 assert(board->size_valid);
305 assert(board->surface_valid);
306 assert(line < board->height);
307 assert(column < board->width);
308 assert(item != NULL);
310 *item = board->surface[line][column];
312 assert(*item == board->surface[line][column]);
315 /* Write an item in position (column,line) of the board. At the same time update
316 * any output medium.
318 void poke_item (unsigned int line, unsigned int column, board_t *board, item_t item)
320 assert(board != NULL);
321 assert(board->size_valid);
322 assert(board->surface_valid);
323 assert(line < board->height);
324 assert(column < board->width);
326 board->surface[line][column] = item;
328 print_item (line, column, item);
330 assert(board->surface[line][column] == item);
333 /* Fetch an item from somewhere. The purpose of this functin is to allow the
334 * user to load one pattern every time the program runs. For now all items are
335 * random (sort of).
337 void fetch_item(unsigned int line, unsigned int column, item_t *item)
339 assert(item != NULL);
341 int number;
343 /* generate one block in every FREE_PER_ITEM board cells */
344 number = random()%FREE_PER_ITEM;
345 switch (number) {
346 case 0:
347 *item = A;
348 break;
349 default:
350 *item = EMPTY;
351 break;
354 void setup_board(board_t *board)
356 assert (board->size_valid);
357 assert (!board->surface_valid);
359 unsigned int column, line;
360 item_t item;
362 //printf("Preparing board.\n");
364 /* allocate space for the board */
365 board->surface = (item_t **)calloc (board->height, sizeof(board->surface));
366 board->lock = (pthread_mutex_t **)calloc (board->height, sizeof(board->lock));
367 if (board->surface == NULL) {
368 perror("board_setup");
369 return;
371 if (board->lock == NULL) {
372 perror("board_setup");
373 return;
375 for (line = 0; line < board->height; ++line) {
376 board->surface[line] = (item_t *)calloc (board->width, sizeof(item_t));
377 board->lock[line] = (pthread_mutex_t *)calloc (board->width, sizeof(pthread_mutex_t));
378 if (board->surface[line] == NULL) {
379 perror("board_setup");
380 return;
382 if (board->lock[line] == NULL) {
383 perror("board_setup");
384 return;
387 board->surface_valid = true;
389 //printf("Printing board.\n");
390 print_board(board);
391 //printf("Board printed.\n");
393 /* fill the board with initial values */
394 for (line = 0; line < board->height; ++line) {
395 for (column = 0; column < board->width; ++column) {
396 fetch_item(line, column, &item);
397 poke_item(line, column, board, item);
398 pthread_mutex_init((board->lock)[line]+column, NULL);
402 assert (board->surface_valid);
405 void finish_board (board_t *board)
407 assert (board->surface_valid);
409 int line;
411 for (line = 0; line < board->height; ++line) {
412 free (board->surface[line]);
413 free (board->lock[line]);
415 free (board->surface);
416 free (board->lock);
417 board->surface_valid = false;
419 erase_board(board);
421 assert (!board->surface_valid);
424 void count_items(board_t *board, item_t *item, unsigned int *nitems)
426 assert (board != NULL);
427 assert (item != NULL);
428 assert (nitems != NULL);
430 unsigned int line, column;
431 item_t this_item;
432 unsigned int count;
434 count = 0;
435 for (line = 0; line < board->height; ++line) {
436 for (column = 0; column < board->width; ++column) {
437 peek_item(line, column, board, &this_item);
438 if (this_item == *item) count++;
442 *nitems = count;
445 void print_mutexes(board_t *board)
447 int line, column;
449 for (line = 0; line < board->height; ++line) {
450 for (column = 0; column < board->width; ++column) {
451 print_item(line, column, board->surface[line][column]);
452 if (pthread_mutex_trylock((board->lock)[line]+column) == 0) {
453 printf("U");
454 } else {
455 printf("L");
461 int main (int argc, char *argv[])
463 board_t board;
464 unsigned int nitems_begin, nitems_finish;
465 pthread_attr_t thread_attributes;
466 pthread_t sorter[NSORTERS];
467 item_t item;
468 unsigned int n;
469 unsigned int line, column;
471 board.width = WIDTH;
472 board.height = HEIGHT;
473 board.size_valid = true;
474 board.surface_valid = false;
476 /* intialize random number generator */
477 srandom((unsigned int)time(NULL));
479 setup_board (&board);
480 sleep(1);
482 item = A;
483 count_items(&board, &item, &nitems_begin);
485 /* launch sorters */
486 pthread_attr_init(&thread_attributes);
487 for (n = 0; n < NSORTERS; ++n) {
488 pthread_create(sorter+n, &thread_attributes, launch_sorter, (void *)(&board));
491 /* wait for keypress (return) */
492 getchar();
494 count_items(&board, &item, &nitems_finish);
495 //print_mutexes(&board);
497 /* make shure the sorters didn't ate items (they can have one with
498 * themselves) */
499 assert((nitems_begin - nitems_finish) <= NSORTERS);
501 /* lock the board so sorters finish any pending operation */
502 for (line = 0; line < board.height; ++line) {
503 for (column = 0; column < board.width; ++column) {
504 pthread_mutex_lock((board.lock)[line]+column);
508 for (n = 0; n < NSORTERS; ++n) {
509 pthread_cancel(sorter[n]);
512 finish_board (&board);
514 printf("All good.\n");
516 exit(0);