Introduced debug function print_mutexes.
[funnysort.git] / funnysort.c
blob89878c4860398583a8b981c736c1d48e4dc93a3b
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 /* lock position to where the sorter may move */
88 pthread_mutex_lock((board->lock)[newline]+newcolumn);
89 peek_item(newline, newcolumn, board, &item);
90 if (item != EMPTY) {
91 /* unlock if position if not available */
92 pthread_mutex_unlock((board->lock)[newline]+newcolumn);
94 ++ncycles;
95 } while ((item != EMPTY) && (ncycles < SORTER_MOVE_TRIES));
97 /* If sorter failed to move, respawn in random position */
98 while (item != EMPTY) {
99 newline = random()%HEIGHT;
100 newcolumn = random()%WIDTH;
102 /* lock position to where the sorter may move */
103 pthread_mutex_lock((board->lock)[newline]+newcolumn);
104 peek_item(newline, newcolumn, board, &item);
105 if (item != EMPTY) {
106 /* unlock if position if not available */
107 pthread_mutex_unlock((board->lock)[newline]+newcolumn);
111 /* Update board. */
113 /* Don't reset the item if sorter just droped one. */
114 /* If an item was droped, it will be different from SORTER. */
115 peek_item(sorter->line, sorter->column, board, &sorter_item);
116 if (sorter_item == SORTER) {
117 poke_item (sorter->line, sorter->column, board, EMPTY);
119 poke_item(newline, newcolumn, board, SORTER);
120 sorter->line = newline;
121 sorter->column = newcolumn;
123 pthread_mutex_unlock((board->lock)[newline]+newcolumn);
125 assert(sorter->column < WIDTH);
126 assert(sorter->line < HEIGHT);
129 void *launch_sorter (void *board)
131 int itera;
132 item_t item;
133 unsigned int line;
134 unsigned int column;
135 sorter_t sorter;
137 /* Place sorter in some free position. */
138 do {
139 line = random()%HEIGHT;
140 column = random()%WIDTH;
141 peek_item(line, column, board, &item);
142 } while (item != EMPTY);
143 poke_item(line, column, board, SORTER);
144 sorter.line = line;
145 sorter.column = column;
147 for (itera = 0; itera <= NCYCLES; ++itera) {
148 //for (itera = 0; 0==0; ++itera) {
149 do {
150 move_sorter(board, &sorter);
151 pick_item(sorter.line, sorter.column, board, &item);
153 usleep(SLEEP_TIME);
154 } while (item == EMPTY);
156 /* because the sorter allways moves, if it drops the item it will not
157 * remain on the top of it */
158 while (item != EMPTY) {
159 line = sorter.line;
160 column = sorter.column;
162 // Because the sorter may drop an item, it's current position must
163 // be locked until after it moves.
164 pthread_mutex_lock((((board_t *)board)->lock)[line]+column);
165 drop_item(sorter.line, sorter.column, board, &item);
166 move_sorter(board, &sorter);
167 pthread_mutex_unlock((((board_t *)board)->lock)[line]+column);
169 usleep(SLEEP_TIME);
172 /* leave the premisses */
173 //for (r = 0; (r < 10); ++r) {
174 // move(board, sorter);
178 return NULL;
181 /* Try to pick an item. Don't pick other sorters :) */
182 void pick_item (unsigned int line, unsigned int column, board_t *board, item_t *item)
184 assert (board != NULL);
185 assert (board->size_valid);
186 assert (board->surface_valid);
187 assert (line < board->height);
188 assert (column < board->width);
189 assert (item != NULL);
190 assert (*item == EMPTY);
192 int newline, newcolumn;
193 item_t newitem;
194 int h, v; /* h: horizontal; v: vertical */
196 for (h = -1; (h <= 1) && (*item == EMPTY); ++h) {
197 for (v = -1; (v <= 1) && (*item == EMPTY); ++v) {
198 /* newline and newcolumn need to be allawys positive */
199 newline = (line+(h+HEIGHT))%HEIGHT;
200 newcolumn = (column+(v+WIDTH))%WIDTH;
202 pthread_mutex_lock((board->lock)[newline]+newcolumn);
203 peek_item(newline, newcolumn, board, &newitem);
204 if((newitem != EMPTY) && (newitem != SORTER)) {
205 *item = newitem;
206 poke_item(newline, newcolumn, board, EMPTY);
207 /* we have the item */
209 pthread_mutex_unlock((board->lock)[newline]+newcolumn);
213 /* we either pick an item or leave it as it is */
214 assert ((*item == newitem) || (*item == EMPTY));
215 assert (*item != SORTER);
218 /* Try to drop one item. The item needs to be droped in a position adjacent to
219 * a similar item.
221 void drop_item (unsigned int line, unsigned int column, board_t *board, item_t *item)
223 assert (board != NULL);
224 assert (board->size_valid);
225 assert (board->surface_valid);
226 assert (line < board->height);
227 assert (column < board->width);
228 assert (item != NULL);
229 assert (*item != EMPTY);
230 assert (*item != SORTER);
232 int newline, newcolumn;
233 int h, v; /* h: horizontal, v: vertical */
234 item_t newitem;
236 for (h = -1; (h <= 1) && (*item != EMPTY); ++h) {
237 for (v = -1; (v <= 1) && (*item != EMPTY); ++v) {
238 /* newline and newcolumn need to be allawys positive */
239 newline = (line+h+HEIGHT)%HEIGHT;
240 newcolumn = (column+v+WIDTH)%WIDTH;
242 pthread_mutex_lock((board->lock)[newline]+newcolumn);
243 peek_item(newline, newcolumn, board, &newitem);
244 if(newitem == *item) {
245 poke_item(line, column, board, newitem);
246 *item = EMPTY;
247 /* and the item is down */
249 pthread_mutex_unlock((board->lock)[newline]+newcolumn);
254 /* Print the base of the board, without any items. */
255 void print_board(board_t *board)
257 assert(board->size_valid);
258 assert(board->surface_valid);
260 conglClearScreen();
261 conglMoveCursor(1, 1);
264 void print_item(unsigned int line, unsigned int column, item_t item)
266 struct properties_s {
267 conglColor_t bg;
268 conglColor_t fg;
269 char symbol;
271 typedef struct properties_s properties_t;
273 properties_t properties[] = {
274 {congl_magenta, congl_white, ' '},
275 {congl_green, congl_white, ' '},
276 {congl_black, congl_white, ' '}
277 //{'i'}, {'f'}, {'s'}
280 properties_t p;
282 p = properties[item];
283 conglDrawCharFull(line+1, column+1, p.symbol, p.fg, p.bg);
285 //printf("%c: %d,%d\n", p.symbol, column, line);
288 /* Read an item from the board. */
289 void peek_item (unsigned int line, unsigned int column, board_t *board, item_t *item)
291 assert(board != NULL);
292 assert(board->size_valid);
293 assert(board->surface_valid);
294 assert(line < board->height);
295 assert(column < board->width);
296 assert(item != NULL);
298 *item = board->surface[line][column];
300 assert(*item == board->surface[line][column]);
303 /* Write an item in position (column,line) of the board. At the same time update
304 * any output medium.
306 void poke_item (unsigned int line, unsigned int column, board_t *board, item_t item)
308 assert(board != NULL);
309 assert(board->size_valid);
310 assert(board->surface_valid);
311 assert(line < board->height);
312 assert(column < board->width);
314 board->surface[line][column] = item;
316 print_item (line, column, item);
318 assert(board->surface[line][column] == item);
321 /* Fetch an item from somewhere. The purpose of this functin is to allow the
322 * user to load one pattern every time the program runs. For now all items are
323 * random (sort of).
325 void fetch_item(unsigned int line, unsigned int column, item_t *item)
327 assert(item != NULL);
329 int number;
331 /* generate one block in every FREE_PER_ITEM board cells */
332 number = random()%FREE_PER_ITEM;
333 switch (number) {
334 case 0:
335 *item = A;
336 break;
337 default:
338 *item = EMPTY;
339 break;
342 void setup_board(board_t *board)
344 assert (board->size_valid);
345 assert (!board->surface_valid);
347 unsigned int column, line;
348 item_t item;
350 //printf("Preparing board.\n");
352 /* allocate space for the board */
353 board->surface = (item_t **)calloc (board->height, sizeof(board->surface));
354 board->lock = (pthread_mutex_t **)calloc (board->height, sizeof(board->lock));
355 if (board->surface == NULL) {
356 perror("board_setup");
357 return;
359 if (board->lock == NULL) {
360 perror("board_setup");
361 return;
363 for (line = 0; line < board->height; ++line) {
364 board->surface[line] = (item_t *)calloc (board->width, sizeof(item_t));
365 board->lock[line] = (pthread_mutex_t *)calloc (board->width, sizeof(pthread_mutex_t));
366 if (board->surface[line] == NULL) {
367 perror("board_setup");
368 return;
370 if (board->lock[line] == NULL) {
371 perror("board_setup");
372 return;
375 board->surface_valid = true;
377 //printf("Printing board.\n");
378 print_board(board);
379 //printf("Board printed.\n");
381 /* fill the board with initial values */
382 for (line = 0; line < board->height; ++line) {
383 for (column = 0; column < board->width; ++column) {
384 fetch_item(line, column, &item);
385 poke_item(line, column, board, item);
386 pthread_mutex_init((board->lock)[line]+column, NULL);
390 assert (board->surface_valid);
393 void finish_board (board_t *board)
395 assert (board->surface_valid);
397 int line;
399 for (line = 0; line < board->height; ++line) {
400 free (board->surface[line]);
401 free (board->lock[line]);
403 free (board->surface);
404 free (board->lock);
405 board->surface_valid = false;
407 erase_board(board);
409 assert (!board->surface_valid);
412 void count_items(board_t *board, item_t *item, unsigned int *nitems)
414 assert (board != NULL);
415 assert (item != NULL);
416 assert (nitems != NULL);
418 unsigned int line, column;
419 item_t this_item;
420 unsigned int count;
422 count = 0;
423 for (line = 0; line < board->height; ++line) {
424 for (column = 0; column < board->width; ++column) {
425 peek_item(line, column, board, &this_item);
426 if (this_item == *item) count++;
430 *nitems = count;
433 void print_mutexes(board_t *board)
435 int line, column;
437 for (line = 0; line < board->height; ++line) {
438 for (column = 0; column < board->width; ++column) {
439 print_item(line, column, board->surface[line][column]);
440 if (pthread_mutex_trylock((board->lock)[line]+column) == 0) {
441 printf("U");
442 } else {
443 printf("L");
449 int main (int argc, char *argv[])
451 board_t board;
452 unsigned int nitems_begin, nitems_finish;
453 pthread_attr_t thread_attributes;
454 pthread_t sorter[NSORTERS];
455 item_t item;
456 unsigned int n;
458 board.width = WIDTH;
459 board.height = HEIGHT;
460 board.size_valid = true;
461 board.surface_valid = false;
463 /* intialize random number generator */
464 srandom((unsigned int)time(NULL));
466 setup_board (&board);
467 sleep(1);
469 item = A;
470 count_items(&board, &item, &nitems_begin);
472 /* launch sorters */
473 pthread_attr_init(&thread_attributes);
474 for (n = 0; n < NSORTERS; ++n) {
475 pthread_create(sorter+n, &thread_attributes, launch_sorter, (void *)(&board));
478 /* wait for keypress (return) */
479 getchar();
480 printf("THE END");
482 count_items(&board, &item, &nitems_finish);
483 print_mutexes(&board);
485 /* make shure the sorters didn't ate items (they can have one with
486 * themselves) */
487 assert((nitems_begin - nitems_finish) <= NSORTERS);
489 for (n = 0; n < NSORTERS; ++n) {
490 pthread_cancel(sorter[n]);
493 finish_board (&board);
495 printf("All good.\n");
497 exit(0);