/* -*- Mode: C; tab-width: 4 -*- */ /* polyominoes --- Shows attempts to place polyominoes into a rectangle */ #if !defined( lint ) && !defined( SABER ) static const char sccsid[] = "@(#)polyominoes.c 5.01 2000/12/18 xlockmore"; #endif /* * Copyright (c) 2000 by Stephen Montgomery-Smith * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * This file is provided AS IS with no warranties of any kind. The author * shall have no liability with respect to the infringement of copyrights, * trade secrets or any patents by this file or any part thereof. In no * event will the author be liable for any lost revenue or profits or * other special, indirect and consequential damages. * * Revision History: * 07-Jan-2001: Made improvement to the search algorithm for the puzzles * involving identical polyominoes (via the variable * reason_to_not_attach). By Stephen Montgomery-Smith. * 20-Dec-2000: Added more puzzles at David Bagley's request. * 27-Nov-2000: Adapted from euler2d.c Copyright (c) 2000 by Stephen * Montgomery-Smith * 05-Jun-2000: Adapted from flow.c Copyright (c) 1996 by Tim Auckland * 18-Jul-1996: Adapted from swarm.c Copyright (c) 1991 by Patrick J. Naughton. * 31-Aug-1990: Adapted from xswarm by Jeff Butterworth. (butterwo@ncsc.org) */ #ifdef STANDALONE #define MODE_polyominoes #define PROGCLASS "Polyominoes" #define HACK_INIT init_polyominoes #define HACK_DRAW draw_polyominoes #define polyominoes_opts xlockmore_opts #define DEFAULTS "*delay: 1000 \n" \ "*count: 1024 \n" \ "*cycles: 3000 \n" \ "*ncolors: 200 \n" #define SMOOTH_COLORS #include "xlockmore.h" /* in xscreensaver distribution */ #else /* STANDALONE */ #include "xlock.h" /* in xlockmore distribution */ #endif /* STANDALONE */ #ifdef MODE_polyominoes #define DEF_IDENTICAL "False" #define DEF_PLAIN "False" static Bool identical; static Bool plain; static XrmOptionDescRec opts[] = { {(char *) "-identical", (char *) ".polyominoes.identical", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+identical", (char *) ".polyominoes.identical", XrmoptionNoArg, (caddr_t) "off"}, {(char *) "-plain", (char *) ".polyominoes.plain", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+plain", (char *) ".polyominoes.plain", XrmoptionNoArg, (caddr_t) "off"} }; static argtype vars[] = { {(void *) &identical, (char *) "identical", (char *) "Identical", (char *) DEF_IDENTICAL, t_Bool}, {(void *) & plain, (char *) "plain", (char *) "Plain", (char *) DEF_PLAIN, t_Bool} }; static OptionStruct desc[] = { {(char *) "-/+identical", (char *) "turn on/off puzzles where the polyomino pieces are identical"}, {(char *) "-/+plain", (char *) "turn on/off plain pieces"} }; ModeSpecOpt polyominoes_opts = {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc}; #ifdef USE_MODULES ModStruct polyominoes_description = { "polyominoes", "init_polyominoes", "draw_polyominoes", "release_polyominoes", "refresh_polyominoes", "init_polyominoes", (char *) NULL, &polyominoes_opts, 6000, 1, 8192, 1, 64, 1.0, "", "Shows attempts to place polyominoes into a rectangle", 0, NULL }; #endif /* Each polyomino is described by several quantities: len: the number of squares in the polyomino; point: the list of points; tranform_len: the number of items in transform_list; transform_list: a list of transformations that need to be done in order to get all rotations and reflections (see the function transform below); max_white: the maximum number of white squares covered if polyomino is placed on a chess board; color: it's display color; attached: whether it is currently attached to the rectangle; attach_point: point on rectangle where attached; point_no: the number of the point where it is attached; transform_index: which element of transform_list is currently being used. */ typedef struct {int x,y;} point_type; typedef struct {int len; point_type *point; int transform_len, transform_list[8], max_white; unsigned long color; int attached; point_type attach_point; int point_no, transform_index;} polyomino_type; typedef struct _polyominoesstruct{ int wait; int width, height; unsigned border_color; int mono; polyomino_type *polyomino; int nr_polyominoes; Bool identical, use3D; int *attach_list; int nr_attached; /* The array that tells where the polyominoes are attached. */ int *array, *changed_array; /* These specify the dimensions of how things appear on the screen. */ int box, x_margin, y_margin; /* These booleans decide in which way to try to attach the polyominoes. */ int left_right, top_bottom; /* Bitmaps for display purposes. */ int use_bitmaps; XImage *bitmaps[256]; /* Structures used for display purposes if there is not enough memory to allocate bitmaps (or if the screen is small). */ XSegment *lines; XRectangle *rectangles; /* A procedure that may be used to see if certain configurations are permissible. */ int (*check_ok)(struct _polyominoesstruct *sp); /* Tells program that solutions are invariant under 180 degree rotation. */ int rot180; /* This is a variable used in the case that all the polyominoes are identical that will further prune the search tree. Essentially it will be int reason_to_not_attach[nr_polynominoes][nr_polynominoes]; Let me first explain the effect it is trying to overcome. Often in the search process, the computer will first try to fit shapes into a region (call it A), and then into another region (call it B) that is essentially disjoint from A. But it may be that it just is not possible to fit shapes into region B. So it fits something into A, and then tries to fit something into B. Failing it fits something else into A, and then tried again to fit something into B. Thus the program is trying again and again to fit something into B, when it should have figured out the first time that it was impossible. To overcome this, everytime we try to attach a piece, we collect the reasons why it cannot be attached (a boolean for each piece that got in the way). If we see that a piece cannot be attached, we detach the other pieces until we have detached at least one piece for which the boolean reason_to_not_attach is set. */ int *reason_to_not_attach; int counter; } polyominoesstruct; #define ARRAY(x,y) (sp->array[(x)*sp->height+(y)]) #define CHANGED_ARRAY(x,y) (sp->changed_array[(x)*sp->height+(y)]) #define ARRAY_P(p) (sp->array[(p).x*sp->height+(p).y]) #define CHANGED_ARRAY_P(p) (sp->changed_array[(p).x*sp->height+(p).y]) #define ARR(x,y) (((x)<0||(x)>=sp->width||(y)<0||(y)>=sp->height)?-2:ARRAY(x,y)) #define REASON_TO_NOT_ATTACH(x,y) (sp->reason_to_not_attach[(x)*sp->nr_polyominoes+(y)]) #define ROUND8(n) ((((n)+7)/8)*8) /* Defines to index the bitmaps. A set bit indicates that an edge or corner is required. */ #define LEFT (1<<0) #define RIGHT (1<<1) #define UP (1<<2) #define DOWN (1<<3) #define LEFT_UP (1<<4) #define LEFT_DOWN (1<<5) #define RIGHT_UP (1<<6) #define RIGHT_DOWN (1<<7) #define IS_LEFT(n) ((n) & LEFT) #define IS_RIGHT(n) ((n) & RIGHT) #define IS_UP(n) ((n) & UP) #define IS_DOWN(n) ((n) & DOWN) #define IS_LEFT_UP(n) ((n) & LEFT_UP) #define IS_LEFT_DOWN(n) ((n) & LEFT_DOWN) #define IS_RIGHT_UP(n) ((n) & RIGHT_UP) #define IS_RIGHT_DOWN(n) ((n) & RIGHT_DOWN) /* Defines to access the bitmaps. */ #define BITNO(x,y) ((x)+(y)*ROUND8(sp->box)) #define SETBIT(n,x,y) {data[BITNO(x,y)/8] |= 1<<(BITNO(x,y)%8);} #define RESBIT(n,x,y) {data[BITNO(x,y)/8] &= ~(1<<(BITNO(x,y)%8));} #define TWOTHIRDSBIT(n,x,y) {if ((x+y-1)%3) SETBIT(n,x,y) else RESBIT(n,x,y)} #define HALFBIT(n,x,y) {if ((x-y)%2) SETBIT(n,x,y) else RESBIT(n,x,y)} #define THIRDBIT(n,x,y) {if (!((x-y-1)%3)) SETBIT(n,x,y) else RESBIT(n,x,y)} #define THREEQUARTERSBIT(n,x,y) \ {if ((y%2)||((x+2+y/2+1)%2)) SETBIT(n,x,y) else RESBIT(n,x,y)} #define NOTHALFBIT(n,x,y) {if ((x-y)%2) RESBIT(n,x,y) else SETBIT(n,x,y)} /* Parameters for bitmaps. */ #define G ((sp->box/45)+1) /* 1/2 of gap between polyominoes. */ #define T ((sp->box<=12)?1:(G*2)) /* Thickness of walls of polyominoes. */ #define R ((sp->box<=12)?1:(G*6)) /* Amount of rounding. */ #define RT ((sp->box<=12)?1:(G*3)) /* Thickness of wall of rounded parts. Here 3 is an approximation to 2 sqrt(2). */ #define RR 0 /* Roof ridge thickness */ #if 0 /* A list of those bitmaps we need to create to display any pentomino. */ /* (not used right now because it does not seem to work for hexonimoes.) */ static int bitmaps_needed[] = { LEFT_UP|LEFT_DOWN|RIGHT_UP|RIGHT_DOWN, LEFT|RIGHT_UP|RIGHT_DOWN, RIGHT|LEFT_UP|LEFT_DOWN, UP|LEFT_DOWN|RIGHT_DOWN, DOWN|LEFT_UP|RIGHT_UP, LEFT|RIGHT_UP, RIGHT|LEFT_UP, UP|LEFT_DOWN, DOWN|LEFT_UP, LEFT|RIGHT_DOWN, RIGHT|LEFT_DOWN, UP|RIGHT_DOWN, DOWN|RIGHT_UP, /* These needed for hexonimoes*/ LEFT, RIGHT, UP, DOWN, LEFT_DOWN|RIGHT_UP|RIGHT_DOWN, LEFT_UP|RIGHT_UP|RIGHT_DOWN, LEFT_UP|LEFT_DOWN|RIGHT_DOWN, LEFT_UP|LEFT_DOWN|RIGHT_UP, LEFT|UP|RIGHT_DOWN, LEFT|DOWN|RIGHT_UP, RIGHT|UP|LEFT_DOWN, RIGHT|DOWN|LEFT_UP, LEFT|UP, LEFT|DOWN, RIGHT|UP, RIGHT|DOWN, LEFT|RIGHT, UP|DOWN, RIGHT|UP|DOWN, LEFT|UP|DOWN, LEFT|RIGHT|DOWN, LEFT|RIGHT|UP, -1 }; static int bitmap_needed(int n) { int i; for (i=0;bitmaps_needed[i]!=-1;i++) if (n == bitmaps_needed[i]) return 1; return 0; } #endif /* Some debugging routines. static void print_board(polyominoesstruct *sp) { int x,y; for (y=0;yheight;y++) { for (x=0;xwidth;x++) if (ARRAY(x,y) == -1) (void) fprintf(stderr," "); else (void) fprintf(stderr,"%c",'a'+ARRAY(x,y)); (void) fprintf(stderr,"\n"); } (void) fprintf(stderr,"\n"); } static void print_points(point_type *point, int len) { int i; for (i=0;ix=in.x-offset.x+attach_point.x; out->y=in.y-offset.y+attach_point.y; break; case 1: out->x=-(in.y-offset.y)+attach_point.x; out->y=in.x-offset.x+attach_point.y; break; case 2: out->x=-(in.x-offset.x)+attach_point.x; out->y=-(in.y-offset.y)+attach_point.y; break; case 3: out->x=in.y-offset.y+attach_point.x; out->y=-(in.x-offset.x)+attach_point.y; break; case 4: out->x=-(in.x-offset.x)+attach_point.x; out->y=in.y-offset.y+attach_point.y; break; case 5: out->x=in.y-offset.y+attach_point.x; out->y=in.x-offset.x+attach_point.y; break; case 6: out->x=in.x-offset.x+attach_point.x; out->y=-(in.y-offset.y)+attach_point.y; break; case 7: out->x=-(in.y-offset.y)+attach_point.x; out->y=-(in.x-offset.x)+attach_point.y; break; } } static int first_poly_no(polyominoesstruct *sp) { int poly_no; poly_no = 0; while(poly_nonr_polyominoes && sp->polyomino[poly_no].attached) poly_no++; return poly_no; } static void next_poly_no(polyominoesstruct *sp, int *poly_no) { if (sp->identical) { *poly_no = sp->nr_polyominoes; } else { do { (*poly_no)++; } while (*poly_nonr_polyominoes && sp->polyomino[*poly_no].attached); } } /* check_all_regions_multiple_of looks for connected regions of blank spaces, and returns 0 if it finds a connected region containing a number of blanks that is not a multiple of n. */ static void count_adjacent_blanks(polyominoesstruct *sp, int *count, int x, int y, int blank_mark) { if (ARRAY(x,y) == -1) { (*count)++; ARRAY(x,y) = blank_mark; if (x>=1) count_adjacent_blanks(sp, count,x-1,y,blank_mark); if (xwidth-1) count_adjacent_blanks(sp, count,x+1,y,blank_mark); if (y>=1) count_adjacent_blanks(sp, count,x,y-1,blank_mark); if (yheight-1) count_adjacent_blanks(sp, count,x,y+1,blank_mark); } } static int check_all_regions_multiple_of(polyominoesstruct *sp, int n) { int x,y,count,good = 1; for (x=0;xwidth && good;x++) for (y=0;yheight && good;y++) { count = 0; count_adjacent_blanks(sp, &count,x,y,-2); good = count%n == 0; } for (x=0;xwidth;x++) for (y=0;yheight;y++) if (ARRAY(x,y) == -2) ARRAY(x,y) = -1; return good; } static int check_all_regions_positive_combination_of(polyominoesstruct *sp, int m, int n) { int x,y,count,good = 1; for (x=0;xwidth && good;x++) for (y=0;yheight && good;y++) { count = 0; count_adjacent_blanks(sp, &count,x,y,-2); good = 0; for (;count>=0 && !good;count-=m) good = count%n == 0; } for (x=0;xwidth;x++) for (y=0;yheight;y++) if (ARRAY(x,y) == -2) ARRAY(x,y) = -1; return good; } static int find_smallest_blank_component(polyominoesstruct *sp) { int x,y,size,smallest_size,blank_mark,smallest_mark; smallest_mark = blank_mark = -10; smallest_size = 1000000000; for (x=0;xwidth;x++) for (y=0;yheight;y++) if (ARRAY(x,y) == -1) { size = 0; count_adjacent_blanks(sp, &size,x,y,blank_mark); if (sizewidth;x++) for (y=0;yheight;y++) { if (ARRAY(x,y) == -1 && (x+y)%2) whites++; if (ARRAY(x,y) == -1 && (x+y+1)%2) blacks++; } for (poly_no=0;poly_nonr_polyominoes;poly_no++) if (!sp->polyomino[poly_no].attached) { max_white += sp->polyomino[poly_no].max_white; min_white += sp->polyomino[poly_no].len - sp->polyomino[poly_no].max_white; } return (min_white <= blacks && min_white <= whites && blacks <= max_white && whites <= max_white); } /* This routine looks at the point (x,y) and sees how many polyominoes and all their various transforms may be attached there. */ static int score_point(polyominoesstruct *sp, int x, int y, int min_score_so_far) { int poly_no, point_no, transform_index, i, attachable; point_type attach_point, target_point; int score = 0; if (x>=1 && xwidth-1 && y>=1 && yheight-1 && ARRAY(x-1,y-1)<0 && ARRAY(x-1,y)<0 && ARRAY(x-1,y+1)<0 && ARRAY(x+1,y-1)<0 && ARRAY(x+1,y)<0 && ARRAY(x+1,y+1)<0 && ARRAY(x,y-1)<0 && ARRAY(x,y+1)<0) return 10000; attach_point.x = x; attach_point.y = y; for (poly_no=first_poly_no(sp);poly_nonr_polyominoes;next_poly_no(sp,&poly_no)) if (!sp->polyomino[poly_no].attached) { for (point_no=0;point_nopolyomino[poly_no].len;point_no++) for (transform_index=0;transform_indexpolyomino[poly_no].transform_len;transform_index++) { attachable = 1; for (i=0;ipolyomino[poly_no].len;i++) { transform(sp->polyomino[poly_no].point[i], sp->polyomino[poly_no].point[point_no], sp->polyomino[poly_no].transform_list[transform_index], attach_point, &target_point); if ( ! ((target_point.x>=0) && (target_point.xwidth) && (target_point.y>=0) && (target_point.yheight) && (ARRAY_P(target_point)<0))) { attachable = 0; break; } } if (attachable) { score++; if (score>=min_score_so_far) return score; } } } return score; } static void find_blank(polyominoesstruct *sp, point_type *point) { int score, worst_score; int x, y; int blank_mark; blank_mark = find_smallest_blank_component(sp); worst_score = 1000000; for (x=0;xwidth;x++) for (y=0;yheight;y++) if (ARRAY(x,y)==blank_mark) { score = 100*score_point(sp,x,y,worst_score); if (score>0) { if (sp->left_right) score += 10*x; else score += 10*(sp->width-1-x); if (sp->top_bottom) score += y; else score += (sp->height-1-y); } if (scorex = x; point->y = y; worst_score = score; } } for (x=0;xwidth;x++) for (y=0;yheight;y++) if (ARRAY(x,y)<0) ARRAY(x,y) = -1; } /* Detaches the most recently attached polyomino. */ static void detach(polyominoesstruct *sp, int *poly_no, int *point_no, int *transform_index, point_type *attach_point, int rot180) { int i; point_type target_point; if (sp->nr_attached == 0) return; sp->nr_attached--; *poly_no = sp->attach_list[sp->nr_attached]; *point_no = sp->polyomino[*poly_no].point_no; *transform_index = sp->polyomino[*poly_no].transform_index; *attach_point = sp->polyomino[*poly_no].attach_point; for (i=0;ipolyomino[*poly_no].len;i++) { transform(sp->polyomino[*poly_no].point[i], sp->polyomino[*poly_no].point[*point_no], sp->polyomino[*poly_no].transform_list[*transform_index]^(rot180<<1), *attach_point, &target_point); ARRAY_P(target_point) = -1; CHANGED_ARRAY_P(target_point) = 1; } sp->polyomino[*poly_no].attached = 0; } /* Attempts to attach a polyomino at point (x,y) at the point_no-th point of that polyomino, using the transform transform_no. Returns 1 if successful. */ static int attach(polyominoesstruct *sp, int poly_no, int point_no, int transform_index, point_type attach_point, int rot180, int *reason_to_not_attach) { point_type target_point; int i; int attachable = 1, worst_reason_not_to_attach = 1000000000; if (rot180) { attach_point.x = sp->width-1-attach_point.x; attach_point.y = sp->height-1-attach_point.y; } if (sp->polyomino[poly_no].attached) return 0; for (i=0;ipolyomino[poly_no].len;i++) { transform(sp->polyomino[poly_no].point[i], sp->polyomino[poly_no].point[point_no], sp->polyomino[poly_no].transform_list[transform_index]^(rot180<<1), attach_point, &target_point); if ( ! ((target_point.x>=0) && (target_point.xwidth) && (target_point.y>=0) && (target_point.yheight) && (ARRAY_P(target_point) == -1))) { if (sp->identical) { attachable = 0; if ((target_point.x>=0) && (target_point.xwidth) && (target_point.y>=0) && (target_point.yheight) && (ARRAY_P(target_point) >= 0) && (ARRAY_P(target_point)identical && !attachable) { if (worst_reason_not_to_attach < 1000000000) reason_to_not_attach[worst_reason_not_to_attach] = 1; return 0; } for (i=0;ipolyomino[poly_no].len;i++) { transform(sp->polyomino[poly_no].point[i], sp->polyomino[poly_no].point[point_no], sp->polyomino[poly_no].transform_list[transform_index]^(rot180<<1), attach_point, &target_point); ARRAY_P(target_point) = poly_no; CHANGED_ARRAY_P(target_point) = 1; } sp->attach_list[sp->nr_attached] = poly_no; sp->nr_attached++; sp->polyomino[poly_no].attached = 1; sp->polyomino[poly_no].point_no = point_no; sp->polyomino[poly_no].attach_point = attach_point; sp->polyomino[poly_no].transform_index = transform_index; if (!sp->check_ok(sp)) { detach(sp,&poly_no,&point_no,&transform_index,&attach_point,rot180); return 0; } return 1; } static int next_attach_try(polyominoesstruct *sp, int *poly_no, int *point_no, int *transform_index) { (*transform_index)++; if (*transform_index>=sp->polyomino[*poly_no].transform_len) { *transform_index = 0; (*point_no)++; if (*point_no>=sp->polyomino[*poly_no].len) { *point_no = 0; next_poly_no(sp,poly_no); if (*poly_no>=sp->nr_polyominoes) { *poly_no = first_poly_no(sp); return 0; } } } return 1; } /******************************************************* Display routines. *******************************************************/ static void draw_without_bitmaps(ModeInfo * mi, polyominoesstruct *sp) { Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); GC gc = MI_GC(mi); int x,y,poly_no,nr_lines,nr_rectangles; XSetLineAttributes(display,gc,sp->box/10+1,LineSolid,CapRound,JoinRound); for (poly_no=-1;poly_nonr_polyominoes;poly_no++) { nr_rectangles = 0; for (x=0;xwidth;x++) for (y=0;yheight;y++) if (CHANGED_ARRAY(x,y) && ARRAY(x,y) == poly_no) { sp->rectangles[nr_rectangles].x = sp->x_margin + sp->box*x; sp->rectangles[nr_rectangles].y = sp->y_margin + sp->box*y; sp->rectangles[nr_rectangles].width = sp->box; sp->rectangles[nr_rectangles].height = sp->box; nr_rectangles++; } if (poly_no == -1) XSetForeground(display, gc, MI_BLACK_PIXEL(mi)); else XSetForeground(display, gc, sp->polyomino[poly_no].color); XFillRectangles(display, window, gc, sp->rectangles, nr_rectangles); } XSetForeground(display, gc, MI_BLACK_PIXEL(mi)); nr_lines = 0; for (x=0;xwidth-1;x++) for (y=0;yheight;y++) { if (ARRAY(x,y) == -1 && ARRAY(x+1,y) == -1 && (CHANGED_ARRAY(x,y) || CHANGED_ARRAY(x+1,y))) { sp->lines[nr_lines].x1 = sp->x_margin + sp->box*(x+1); sp->lines[nr_lines].y1 = sp->y_margin + sp->box*y; sp->lines[nr_lines].x2 = sp->x_margin + sp->box*(x+1); sp->lines[nr_lines].y2 = sp->y_margin + sp->box*(y+1); nr_lines++; } } XDrawSegments(display, window, gc, sp->lines, nr_lines); nr_lines = 0; for (x=0;xwidth;x++) for (y=0;yheight-1;y++) { if (ARRAY(x,y) == -1 && ARRAY(x,y+1) == -1 && (CHANGED_ARRAY(x,y) || CHANGED_ARRAY(x,y+1))) { sp->lines[nr_lines].x1 = sp->x_margin + sp->box*x; sp->lines[nr_lines].y1 = sp->y_margin + sp->box*(y+1); sp->lines[nr_lines].x2 = sp->x_margin + sp->box*(x+1); sp->lines[nr_lines].y2 = sp->y_margin + sp->box*(y+1); nr_lines++; } } XDrawSegments(display, window, gc, sp->lines, nr_lines); XSetForeground(display, gc, MI_WHITE_PIXEL(mi)); XDrawRectangle(display, window, gc, sp->x_margin, sp->y_margin, sp->box*sp->width, sp->box*sp->height); XSetForeground(display, gc, MI_WHITE_PIXEL(mi)); nr_lines = 0; for (x=0;xwidth-1;x++) for (y=0;yheight;y++) { if (ARRAY(x+1,y) != ARRAY(x,y)) { sp->lines[nr_lines].x1 = sp->x_margin + sp->box*(x+1); sp->lines[nr_lines].y1 = sp->y_margin + sp->box*y; sp->lines[nr_lines].x2 = sp->x_margin + sp->box*(x+1); sp->lines[nr_lines].y2 = sp->y_margin + sp->box*(y+1); nr_lines++; } } XDrawSegments(display, window, gc, sp->lines, nr_lines); nr_lines = 0; for (x=0;xwidth;x++) for (y=0;yheight-1;y++) { if (ARRAY(x,y+1) != ARRAY(x,y)) { sp->lines[nr_lines].x1 = sp->x_margin + sp->box*x; sp->lines[nr_lines].y1 = sp->y_margin + sp->box*(y+1); sp->lines[nr_lines].x2 = sp->x_margin + sp->box*(x+1); sp->lines[nr_lines].y2 = sp->y_margin + sp->box*(y+1); nr_lines++; } } XDrawSegments(display, window, gc, sp->lines, nr_lines); XSetLineAttributes(display,gc,1,LineSolid,CapRound,JoinRound); XFlush(display); } static void create_bitmaps(ModeInfo * mi, polyominoesstruct *sp) { int x,y,n; char *data; for (n=0;n<256;n++) { /* Avoid duplication of identical bitmaps. */ if (IS_LEFT_UP(n) && (IS_LEFT(n) || IS_UP(n))) sp->bitmaps[n] = sp->bitmaps[n & ~LEFT_UP]; else if (IS_LEFT_DOWN(n) && (IS_LEFT(n) || IS_DOWN(n))) sp->bitmaps[n] = sp->bitmaps[n & ~LEFT_DOWN]; else if (IS_RIGHT_UP(n) && (IS_RIGHT(n) || IS_UP(n))) sp->bitmaps[n] = sp->bitmaps[n & ~RIGHT_UP]; else if (IS_RIGHT_DOWN(n) && (IS_RIGHT(n) || IS_DOWN(n))) sp->bitmaps[n] = sp->bitmaps[n & ~RIGHT_DOWN]; else /* if (bitmap_needed(n)) */ { data = (char *) malloc(sizeof(char)*(sp->box*ROUND8(sp->box)/8)); if (data == NULL) { sp->use_bitmaps = 0; return; } for (y=0;ybox;y++) for (x=0;xbox;x++) { if (!sp->use3D) { #ifdef SMALL_BELLYBUTTON if (x >= sp->box / 2 && x <= sp->box / 2 + 1 && y >= sp->box / 2 && y <= sp->box / 2 + 1) NOTHALFBIT(n,x,y) else #endif HALFBIT(n,x,y) } else if ((x>=y && x<=sp->box-y-1 && IS_UP(n)) || (x<=y && x<=sp->box-y-1 && ybox/2 && !IS_LEFT(n)) || (x>=y && x>=sp->box-y-1 && ybox/2 && !IS_RIGHT(n))) SETBIT(n,x,y) else if ((x<=y && x<=sp->box-y-1 && IS_LEFT(n)) || (x>=y && x<=sp->box-y-1 && xbox/2 && !IS_UP(n)) || (x<=y && x>=sp->box-y-1 && xbox/2 && !IS_DOWN(n))) TWOTHIRDSBIT(n,x,y) else if ((x>=y && x>=sp->box-y-1 && IS_RIGHT(n)) || (x>=y && x<=sp->box-y-1 && x>=sp->box/2 && !IS_UP(n)) || (x<=y && x>=sp->box-y-1 && x>=sp->box/2 && !IS_DOWN(n))) HALFBIT(n,x,y) else if ((x<=y && x>=sp->box-y-1 && IS_DOWN(n)) || (x<=y && x<=sp->box-y-1 && y>=sp->box/2 && !IS_LEFT(n)) || (x>=y && x>=sp->box-y-1 && y>=sp->box/2 && !IS_RIGHT(n))) THIRDBIT(n,x,y) } if (IS_LEFT(n)) for (y=0;ybox;y++) for (x=G;xbox;y++) for (x=G;xbox-1-x,y) if (IS_UP(n)) for (x=0;xbox;x++) for (y=G;ybox;x++) for (y=G;ybox-1-y) if (IS_LEFT(n)) for (y=0;ybox;y++) for (x=0;xbox;y++) for (x=0;xbox-1-x,y) if (IS_UP(n)) for (x=0;xbox;x++) for (y=0;ybox;x++) for (y=0;ybox-1-y) if (IS_LEFT(n) && IS_UP(n)) for (x=G;x<=G+R;x++) for (y=G;y<=R+2*G-x;y++) { if (x+y>R+2*G-RT) SETBIT(n,x,y) else RESBIT(n,x,y) } if (IS_LEFT(n) && IS_DOWN(n)) for (x=G;x<=G+R;x++) for (y=G;y<=R+2*G-x;y++) { if (x+y>R+2*G-RT) SETBIT(n,x,sp->box-1-y) else RESBIT(n,x,sp->box-1-y) } if (IS_RIGHT(n) && IS_UP(n)) for (x=G;x<=G+R;x++) for (y=G;y<=R+2*G-x;y++) { if (x+y>R+2*G-RT) SETBIT(n,sp->box-1-x,y) else RESBIT(n,sp->box-1-x,y) } if (IS_RIGHT(n) && IS_DOWN(n)) for (x=G;x<=G+R;x++) for (y=G;y<=R+2*G-x;y++) { if (x+y>R+2*G-RT) SETBIT(n,sp->box-1-x,sp->box-1-y) else RESBIT(n,sp->box-1-x,sp->box-1-y) } if (!IS_LEFT(n) && !IS_UP(n) && IS_LEFT_UP(n)) { for (x=0;xbox-1-y) for (x=G;xbox-1-y) for (x=0;xbox-1-y) } if (!IS_RIGHT(n) && !IS_UP(n) && IS_RIGHT_UP(n)) { for (x=0;xbox-1-x,y) for (x=G;xbox-1-x,y) for (x=0;xbox-1-x,y) } if (!IS_RIGHT(n) && !IS_DOWN(n) && IS_RIGHT_DOWN(n)) { for (x=0;xbox-1-x,sp->box-1-y) for (x=G;xbox-1-x,sp->box-1-y) for (x=0;xbox-1-x,sp->box-1-y) } #ifdef LARGE_BELLYBUTTON if (!sp->use3D) { if (!IS_LEFT(n) && !IS_UP(n) && !IS_LEFT_UP(n)) for (x=0;xbox-G-T;ybox;y++) SETBIT(n,x,y) if (!IS_RIGHT(n) && !IS_UP(n) && !IS_RIGHT_UP(n)) for (x=sp->box-G-T;xbox;x++) for(y=0;ybox-G-T;xbox;x++) for(y=sp->box-G-T;ybox;y++) SETBIT(n,x,y) } else #else if (sp->use3D) #endif { if (!IS_LEFT(n) && !IS_UP(n) && !IS_LEFT_UP(n)) for (x=0;xbox/2-RR;x++) for(y=0;ybox/2-RR;y++) THREEQUARTERSBIT(n,x,y) if (!IS_LEFT(n) && !IS_DOWN(n) && !IS_LEFT_DOWN(n)) for (x=0;xbox/2-RR;x++) for(y=sp->box/2+RR;ybox;y++) THREEQUARTERSBIT(n,x,y) if (!IS_RIGHT(n) && !IS_UP(n) && !IS_RIGHT_UP(n)) for (x=sp->box/2+RR;xbox;x++) for(y=0;ybox/2-RR;y++) THREEQUARTERSBIT(n,x,y) if (!IS_RIGHT(n) && !IS_DOWN(n) && !IS_RIGHT_DOWN(n)) for (x=sp->box/2+RR;xbox;x++) for(y=sp->box/2+RR;ybox;y++) THREEQUARTERSBIT(n,x,y) } sp->bitmaps[n] = XCreateImage(MI_DISPLAY(mi), MI_VISUAL(mi), 1, XYBitmap, 0, data, sp->box, sp->box, 8, 0); if (sp->bitmaps[n] == None) { free(data); sp->use_bitmaps = 0; return; } sp->bitmaps[n]->byte_order = MSBFirst; sp->bitmaps[n]->bitmap_unit = 8; sp->bitmaps[n]->bitmap_bit_order = LSBFirst; } } sp->use_bitmaps = 1; } static void draw_with_bitmaps(ModeInfo * mi, polyominoesstruct *sp) { Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); GC gc = MI_GC(mi); int x,y,t,bitmap_index; for (x=0;xwidth;x++) for (y=0;yheight;y++) { if (ARRAY(x,y) == -1) { if (CHANGED_ARRAY(x,y)) { XSetForeground(display, gc, MI_BLACK_PIXEL(mi)); XFillRectangle(display,window,gc, sp->x_margin + sp->box*x, sp->y_margin + sp->box*y, sp->box,sp->box); } } else { XSetForeground(display, gc, sp->polyomino[ARRAY(x,y)].color); bitmap_index = 0; if (ARR(x,y) != ARR(x-1,y)) bitmap_index |= LEFT; if (ARR(x,y) != ARR(x+1,y)) bitmap_index |= RIGHT; if (ARR(x,y) != ARR(x,y-1)) bitmap_index |= UP; if (ARR(x,y) != ARR(x,y+1)) bitmap_index |= DOWN; if (ARR(x,y) != ARR(x-1,y-1)) bitmap_index |= LEFT_UP; if (ARR(x,y) != ARR(x-1,y+1)) bitmap_index |= LEFT_DOWN; if (ARR(x,y) != ARR(x+1,y-1)) bitmap_index |= RIGHT_UP; if (ARR(x,y) != ARR(x+1,y+1)) bitmap_index |= RIGHT_DOWN; (void) XPutImage(display,window,gc, sp->bitmaps[bitmap_index], 0,0, sp->x_margin + sp->box*x, sp->y_margin + sp->box*y, sp->box,sp->box); } } XSetForeground(display, gc, sp->border_color); for (t=G;tx_margin-t-1,sp->y_margin-t-1, sp->box*sp->width+1+2*t, sp->box*sp->height+1+2*t); XFlush(display); } /*************************************************** Routines to initialise and close down polyominoes. ***************************************************/ static void free_bitmaps(polyominoesstruct *sp) { int n; for (n=0;n<256;n++) /* Don't bother to free duplicates */ if (IS_LEFT_UP(n) && (IS_LEFT(n) || IS_UP(n))) sp->bitmaps[n] = None; else if (IS_LEFT_DOWN(n) && (IS_LEFT(n) || IS_DOWN(n))) sp->bitmaps[n] = None; else if (IS_RIGHT_UP(n) && (IS_RIGHT(n) || IS_UP(n))) sp->bitmaps[n] = None; else if (IS_RIGHT_DOWN(n) && (IS_RIGHT(n) || IS_DOWN(n))) sp->bitmaps[n] = None; else if (sp->bitmaps[n] != None) { XDestroyImage(sp->bitmaps[n]); sp->bitmaps[n] = None; } } #define deallocate(p,t) if ((p)!=NULL) {free(p); p=(t*)NULL;} static void free_polyominoes(polyominoesstruct *sp) { int n; for (n=0;nnr_polyominoes;n++) { deallocate(sp->polyomino[n].point, point_type); } deallocate(sp->polyomino, polyomino_type); deallocate(sp->attach_list, int); deallocate(sp->rectangles, XRectangle); deallocate(sp->lines, XSegment); deallocate(sp->reason_to_not_attach, int); deallocate(sp->array, int); deallocate(sp->changed_array, int); free_bitmaps(sp); } #define set_allocate(p,type,size) p = (type *) malloc(size); \ if ((p)==NULL) {free_polyominoes(sp);return 0;} #define copy_polyomino(dst,src,new_rand) \ (dst).len=(src).len; \ (dst).max_white = (src).max_white; \ set_allocate((dst).point,point_type,sizeof(point_type)*(src).len); \ (dst).len = (src).len; \ if (new_rand) \ random_permutation((src).len,perm_point); \ for (i=0;i<(src).len;i++) \ (dst).point[i] = (src).point[perm_point[i]]; \ (dst).transform_len = (src).transform_len; \ if (new_rand) \ random_permutation((src).transform_len,perm_transform); \ for (i=0;i<(src).transform_len;i++) \ (dst).transform_list[i] = (src).transform_list[perm_transform[i]]; \ (dst).attached = 0 /*************************************************** Puzzle specific initialization routines. ***************************************************/ static int check_pentomino_puzzle(polyominoesstruct *sp) { return check_all_regions_multiple_of(sp, 5) && whites_ok(sp); } static int check_hexomino_puzzle(polyominoesstruct *sp) { return check_all_regions_multiple_of(sp, 6) && whites_ok(sp); } static int check_tetr_pentomino_puzzle(polyominoesstruct *sp) { return check_all_regions_positive_combination_of(sp, 5, 4) && whites_ok(sp); } static int check_pent_hexomino_puzzle(polyominoesstruct *sp) { return check_all_regions_positive_combination_of(sp, 6, 5) && whites_ok(sp); } static int check_heptomino_puzzle(polyominoesstruct *sp) { return check_all_regions_multiple_of(sp, 7) && whites_ok(sp); } static int check_octomino_puzzle(polyominoesstruct *sp) { return check_all_regions_multiple_of(sp, 8) && whites_ok(sp); } static int check_dekomino_puzzle(polyominoesstruct *sp) { return check_all_regions_multiple_of(sp, 10) && whites_ok(sp); } static int check_elevenomino_puzzle(polyominoesstruct *sp) { return check_all_regions_multiple_of(sp, 11) && whites_ok(sp); } static struct {int len; point_type point[4]; int transform_len, transform_list[8], max_white;} tetromino[5] = { /* xxxx */ {4, {{0,0}, {1,0}, {2,0}, {3,0}}, 2, {0, 1, -1, -1, -1, -1, -1, -1}, 2}, /* xxx x */ {4, {{0,0}, {1,0}, {2,0}, {2,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 2}, /* xxx x */ {4, {{0,0}, {1,0}, {1,1}, {2,0}}, 4, {0, 1, 2, 3, -1, -1, -1, -1}, 3}, /* xx xx */ {4, {{0,0}, {1,0}, {1,1}, {2,1}}, 4, {0, 1, 4, 5, -1, -1, -1, -1}, 2}, /* xx xx */ {4, {{0,0}, {0,1}, {1,0}, {1,1}}, 1, {0, -1, -1, -1, -1, -1, -1, -1}, 2}}; static struct pentomino_struct {int len; point_type point[5]; int transform_len, transform_list[8], max_white;} pentomino[12] = { /* xxxxx */ {5, {{0,0}, {1,0}, {2,0}, {3,0}, {4,0}}, 2, {0, 1, -1, -1, -1, -1, -1, -1}, 3}, /* xxxx x */ {5, {{0,0}, {1,0}, {2,0}, {3,0}, {3,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* xxxx x */ {5, {{0,0}, {1,0}, {2,0}, {2,1}, {3,0}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* x xxx x */ {5, {{0,0}, {1,0}, {2,-1}, {2,0}, {2,1}}, 4, {0, 1, 2, 3, -1, -1, -1, -1}, 3}, /* xxx xx */ {5, {{0,0}, {1,0}, {2,0}, {2,1}, {3,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* xxx xx */ {5, {{0,0}, {1,0}, {1,1}, {2,0}, {2,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* xxx x x */ {5, {{0,0}, {1,0}, {2,0}, {2,1}, {2,2}}, 4, {0, 1, 2, 3, -1, -1, -1, -1}, 3}, /* x xxx x */ {5, {{0,0}, {1,-1}, {1,0}, {2,0}, {2,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* xxx x x */ {5, {{0,0}, {0,1}, {1,0}, {2,0}, {2,1}}, 4, {0, 1, 2, 3, -1, -1, -1, -1}, 3}, /* x xxx x */ {5, {{0,0}, {0,1}, {1,0}, {2,-1}, {2,0}}, 4, {0, 1, 4, 5, -1, -1, -1, -1}, 3}, /* x xxx x */ {5, {{0,0}, {1,-1}, {1,0}, {1,1}, {2,0}}, 1, {0, -1, -1, -1, -1, -1, -1, -1}, 4}, /* xx xx x */ {5, {{0,0}, {1,0}, {1,1}, {2,1}, {2,2}}, 4, {0, 1, 2, 3, -1, -1, -1, -1}, 3}}; static struct hexomino_struct {int len; point_type point[6]; int transform_len, transform_list[8], max_white;} hexomino[35] = { /* xxxxxx */ {6, {{0,0}, {1,0}, {2,0}, {3,0}, {4,0}, {5,0}}, 2, {0, 1, -1, -1, -1, -1, -1, -1}, 3}, /* xxxxx x */ {6, {{0,0}, {1,0}, {2,0}, {3,0}, {4,0}, {4,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* xxxxx x */ {6, {{0,0}, {1,0}, {2,0}, {3,0}, {3,1}, {4,0}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 4}, /* xxxxx x */ {6, {{0,0}, {1,0}, {2,0}, {2,1}, {3,0}, {4,0}}, 4, {0, 1, 2, 3, -1, -1, -1, -1}, 3}, /* x xxxx x */ {6, {{0,0}, {1,0}, {2,0}, {3,-1}, {3,0}, {3,1}}, 4, {0, 1, 2, 3, -1, -1, -1, -1}, 4}, /* xxxx xx */ {6, {{0,0}, {1,0}, {2,0}, {3,0}, {3,1}, {4,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* xxxx xx */ {6, {{0,0}, {1,0}, {2,0}, {2,1}, {3,0}, {3,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* xxxx x x */ {6, {{0,0}, {1,0}, {2,0}, {3,0}, {3,1}, {3,2}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* x xxxx x */ {6, {{0,0}, {1,0}, {2,-1}, {2,0}, {3,0}, {3,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* xxxx x x */ {6, {{0,0}, {1,0}, {1,1}, {2,0}, {3,0}, {3,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 4}, /* x xxxx x */ {6, {{0,0}, {1,-1}, {1,0}, {2,0}, {3,0}, {3,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 4}, /* xxxx x x */ {6, {{0,0}, {0,1}, {1,0}, {2,0}, {3,0}, {3,1}}, 4, {0, 1, 2, 3, -1, -1, -1, -1}, 3}, /* x xxxx x */ {6, {{0,0}, {0,1}, {1,0}, {2,0}, {3,-1}, {3,0}}, 4, {0, 1, 4, 5, -1, -1, -1, -1}, 3}, /* x xxxx x */ {6, {{0,0}, {1,0}, {2,-1}, {2,0}, {2,1}, {3,0}}, 4, {0, 1, 2, 3, -1, -1, -1, -1}, 4}, /* xxxx xx */ {6, {{0,0}, {1,0}, {1,1}, {2,0}, {2,1}, {3,0}}, 4, {0, 1, 2, 3, -1, -1, -1, -1}, 3}, /* xxxx x x */ {6, {{0,0}, {1,0}, {2,0}, {2,1}, {2,2}, {3,0}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* x xxxx x */ {6, {{0,0}, {1,-1}, {1,0}, {2,0}, {2,1}, {3,0}}, 4, {0, 1, 4, 5, -1, -1, -1, -1}, 3}, /* xx xxx x */ {6, {{0,0}, {1,0}, {2,-1}, {2,0}, {2,1}, {3,-1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* xx xxx x */ {6, {{0,0}, {1,-1}, {1,0}, {2,-1}, {2,0}, {2,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* x xxx x x */ {6, {{0,0}, {0,1}, {1,0}, {2,-1}, {2,0}, {2,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 4}, /* xxx xxx */ {6, {{0,0}, {1,0}, {2,0}, {2,1}, {3,1}, {4,1}}, 4, {0, 1, 4, 5, -1, -1, -1, -1}, 3}, /* xxx xx x */ {6, {{0,0}, {1,0}, {2,0}, {2,1}, {3,1}, {3,2}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* xxx xxx */ {6, {{0,0}, {1,0}, {1,1}, {2,0}, {2,1}, {3,1}}, 4, {0, 1, 4, 5, -1, -1, -1, -1}, 4}, /* xxx xx x */ {6, {{0,0}, {1,0}, {2,0}, {2,1}, {2,2}, {3,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 4}, /* x xxx xx */ {6, {{0,0}, {1,-1}, {1,0}, {2,0}, {2,1}, {3,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 4}, /* xxx x xx */ {6, {{0,0}, {0,1}, {1,0}, {2,0}, {2,1}, {3,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* xxx xx x */ {6, {{0,0}, {1,0}, {1,1}, {2,0}, {2,1}, {2,2}}, 4, {0, 1, 2, 3, -1, -1, -1, -1}, 4}, /* x xxx xx */ {6, {{0,0}, {1,-1}, {1,0}, {1,1}, {2,0}, {2,1}}, 4, {0, 1, 2, 3, -1, -1, -1, -1}, 4}, /* xxx xxx */ {6, {{0,0}, {0,1}, {1,0}, {1,1}, {2,0}, {2,1}}, 2, {0, 1, -1, -1, -1, -1, -1, -1}, 3}, /* xxx x xx */ {6, {{0,0}, {1,0}, {2,0}, {2,1}, {2,2}, {3,2}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* xxx x xx */ {6, {{0,0}, {1,0}, {1,2}, {2,0}, {2,1}, {2,2}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* x xxx x x */ {6, {{0,0}, {0,1}, {1,-1}, {1,0}, {2,0}, {2,1}}, 4, {0, 1, 2, 3, -1, -1, -1, -1}, 3}, /* xx xxx x */ {6, {{0,0}, {0,1}, {1,0}, {2,-1}, {2,0}, {3,-1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* xx xxx x */ {6, {{0,0}, {0,1}, {1,-1}, {1,0}, {2,-1}, {2,0}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}, /* xx xx xx */ {6, {{0,0}, {1,0}, {1,1}, {2,1}, {2,2}, {3,2}}, 4, {0, 1, 4, 5, -1, -1, -1, -1}, 3}}; static struct pentomino_struct one_sided_pentomino[60]; static void make_one_sided_pentomino(void) { int i,j,t,u; j=0; for (i=0;i<18;i++) { one_sided_pentomino[j] = pentomino[i]; for (t=0;t<8;t++) if (one_sided_pentomino[j].transform_list[t]>=4) { one_sided_pentomino[j].transform_len = t; j++; one_sided_pentomino[j] = pentomino[i]; for (u=t;u<8;u++) one_sided_pentomino[j].transform_list[u-t] = one_sided_pentomino[j].transform_list[u]; one_sided_pentomino[j].transform_len -= t; break; } j++; } } static struct hexomino_struct one_sided_hexomino[60]; static void make_one_sided_hexomino(void) { int i,j,t,u; j=0; for (i=0;i<35;i++) { one_sided_hexomino[j] = hexomino[i]; for (t=0;t<8;t++) if (one_sided_hexomino[j].transform_list[t]>=4) { one_sided_hexomino[j].transform_len = t; j++; one_sided_hexomino[j] = hexomino[i]; for (u=t;u<8;u++) one_sided_hexomino[j].transform_list[u-t] = one_sided_hexomino[j].transform_list[u]; one_sided_hexomino[j].transform_len -= t; break; } j++; } } /* Find all the ways of placing all twelve pentominoes into a rectangle whose size is 20x3, 15x4, 12x5 or 10x6. */ static int set_pentomino_puzzle(polyominoesstruct *sp) { int perm_poly[12], perm_point[5], perm_transform[8], i, p; switch (NRAND(4)) { case 0: sp->width = 20; sp->height = 3; break; case 1: sp->width = 15; sp->height = 4; break; case 2: sp->width = 12; sp->height = 5; break; case 3: sp->width = 10; sp->height = 6; break; } sp->nr_polyominoes = 12; set_allocate(sp->polyomino,polyomino_type,12*sizeof(polyomino_type)); random_permutation(12,perm_poly); for (p=0;p<12;p++) { copy_polyomino(sp->polyomino[p],pentomino[perm_poly[p]],1); } sp->check_ok = check_pentomino_puzzle; return 1; } /* Many of the following puzzles are inspired by http://www.xs4all.nl/~gp/PolyominoSolver/Polyomino.html */ /* Find all the ways of placing all eighteen one-sided pentominoes into a rectangle. */ static int set_one_sided_pentomino_puzzle(polyominoesstruct *sp) { int perm_poly[18], perm_point[5], perm_transform[8], i, p; make_one_sided_pentomino(); switch (NRAND(4)) { case 0: sp->width = 30; sp->height = 3; break; case 1: sp->width = 18; sp->height = 5; break; case 2: sp->width = 15; sp->height = 6; break; case 3: sp->width = 10; sp->height = 9; break; } sp->nr_polyominoes = 18; set_allocate(sp->polyomino,polyomino_type,18*sizeof(polyomino_type)); random_permutation(18,perm_poly); for (p=0;p<18;p++) { copy_polyomino(sp->polyomino[p],one_sided_pentomino[perm_poly[p]],1); } sp->check_ok = check_pentomino_puzzle; return 1; } /* Find all the ways of placing all sixty one-sided hexominoes into a rectangle. */ static int set_one_sided_hexomino_puzzle(polyominoesstruct *sp) { int perm_poly[60], perm_point[6], perm_transform[8], i, p; make_one_sided_hexomino(); switch (NRAND(8)) { case 0: sp->width = 20; sp->height = 18; break; case 1: sp->width = 24; sp->height = 15; break; case 2: sp->width = 30; sp->height = 12; break; case 3: sp->width = 36; sp->height = 10; break; case 4: sp->width = 40; sp->height = 9; break; case 5: sp->width = 45; sp->height = 8; break; case 6: sp->width = 60; sp->height = 6; break; case 7: sp->width = 72; sp->height = 5; break; } sp->nr_polyominoes = 60; set_allocate(sp->polyomino,polyomino_type,60*sizeof(polyomino_type)); random_permutation(60,perm_poly); for (p=0;p<60;p++) { copy_polyomino(sp->polyomino[p],one_sided_hexomino[perm_poly[p]],1); } sp->check_ok = check_hexomino_puzzle; return 1; } /* Find all the ways of placing all five tetrominoes and all twelve pentominoes into a rectangle. */ static int set_tetr_pentomino_puzzle(polyominoesstruct *sp) { int perm_poly[17], perm_point[5], perm_transform[8], i, p; switch (NRAND(3)) { case 0: sp->width = 20; sp->height = 4; break; case 1: sp->width = 16; sp->height = 5; break; case 2: sp->width = 10; sp->height = 8; break; } sp->nr_polyominoes = 17; set_allocate(sp->polyomino,polyomino_type,17*sizeof(polyomino_type)); random_permutation(17,perm_poly); for (p=0;p<5;p++) { copy_polyomino(sp->polyomino[perm_poly[p]],tetromino[p],1); } for (p=0;p<12;p++) { copy_polyomino(sp->polyomino[perm_poly[p+5]],pentomino[p],1); } sp->check_ok = check_tetr_pentomino_puzzle; return 1; } /* Find all the ways of placing all twelve pentominoes and all thirty five hexominoes into a rectangle whose size is 18x15. */ static int set_pent_hexomino_puzzle(polyominoesstruct *sp) { int perm_poly[47], perm_point[6], perm_transform[8], i, p; switch (NRAND(5)) { case 0: sp->width = 54; sp->height = 5; break; case 1: sp->width = 45; sp->height = 6; break; case 2: sp->width = 30; sp->height = 9; break; case 3: sp->width = 27; sp->height = 10; break; case 4: sp->width = 18; sp->height = 15; break; } sp->nr_polyominoes = 47; set_allocate(sp->polyomino,polyomino_type,47*sizeof(polyomino_type)); random_permutation(47,perm_poly); for (p=0;p<12;p++) { copy_polyomino(sp->polyomino[perm_poly[p]],pentomino[p],1); } for (p=0;p<35;p++) { copy_polyomino(sp->polyomino[perm_poly[p+12]],hexomino[p],1); } sp->check_ok = check_pent_hexomino_puzzle; return 1; } /* Other puzzles: Science News September 20, 1986 Vol 130, No 12 Science News November 14, 1987 Vol 132, Pg 310 */ /* * **** fills a 10x5 rectangle */ static struct {int len; point_type point[5]; int transform_len, transform_list[8], max_white;} pentomino1 = {5, {{0,0}, {1,0}, {2,0}, {3,0}, {1,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 3}; static int set_pentomino_puzzle1(polyominoesstruct *sp) { int perm_point[5], perm_transform[8], i, p; sp->width = 10; sp->height =5; sp->nr_polyominoes = 10; set_allocate(sp->polyomino,polyomino_type,10*sizeof(polyomino_type)); for (p=0;p<10;p++) { copy_polyomino(sp->polyomino[p],pentomino1,1); } sp->check_ok = check_pentomino_puzzle; return 1; } /* * ***** fills a 24x23 rectangle */ static struct {int len; point_type point[6]; int transform_len, transform_list[8], max_white;} hexomino1 = {6, {{0,0}, {1,0}, {2,0}, {3,0}, {4,0}, {1,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 4}; static int set_hexomino_puzzle1(polyominoesstruct *sp) { int perm_point[6], perm_transform[8], i, p; sp->width = 24; sp->height =23; sp->nr_polyominoes = 92; set_allocate(sp->polyomino,polyomino_type,92*sizeof(polyomino_type)); for (p=0;p<92;p++) { copy_polyomino(sp->polyomino[p],hexomino1,1); } sp->check_ok = check_hexomino_puzzle; return 1; } /* ** ***** fills a 21x26 rectangle (All solutions have 180 degree rotational symmetry) */ static struct {int len; point_type point[7]; int transform_len, transform_list[8], max_white;} heptomino1 = {7, {{0,0}, {1,0}, {2,0}, {3,0}, {4,0}, {1,1}, {2,1}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 4}; static int set_heptomino_puzzle1(polyominoesstruct *sp) { int perm_point[7], perm_transform[8], i, p; sp->rot180 = 1; sp->width = 26; sp->height =21; sp->nr_polyominoes = 78; set_allocate(sp->polyomino,polyomino_type,78*sizeof(polyomino_type)); for (p=0;p<78;p+=2) { copy_polyomino(sp->polyomino[p],heptomino1,1); copy_polyomino(sp->polyomino[p+1],heptomino1,0); } sp->check_ok = check_heptomino_puzzle; return 1; } /* The following puzzles from Polyominoes Puzzles, Patterns, Problems, and Packings Revised (2nd) Edition by Solomon W. Golomb Princeton University Press 1994 */ /* ** ***** fills a 28x19 rectangle */ static int set_heptomino_puzzle2(polyominoesstruct *sp) { int perm_point[7], perm_transform[8], i, p; sp->width = 28; sp->height =19; sp->nr_polyominoes = 76; set_allocate(sp->polyomino,polyomino_type,76*sizeof(polyomino_type)); for (p=0;p<76;p++) { copy_polyomino(sp->polyomino[p],heptomino1,1); } sp->check_ok = check_heptomino_puzzle; return 1; } /* *** **** fills a 25x22 rectangle **** */ static struct {int len; point_type point[11]; int transform_len, transform_list[8], max_white;} elevenomino1 = {11, {{0,0}, {1,0}, {2,0}, {0,1}, {1,1}, {2,1}, {3,1}, {0,2}, {1,2}, {2,2}, {3,2}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 6}; static int set_elevenomino_puzzle1(polyominoesstruct *sp) { int perm_point[11], perm_transform[8], i, p; sp->rot180 = 1; sp->width = 25; sp->height =22; sp->nr_polyominoes = 50; set_allocate(sp->polyomino,polyomino_type,50*sizeof(polyomino_type)); for (p=0;p<50;p+=2) { copy_polyomino(sp->polyomino[p],elevenomino1,1); copy_polyomino(sp->polyomino[p+1],elevenomino1,0); } sp->check_ok = check_elevenomino_puzzle; return 1; } /* * * **** fills 32 x 30 rectangle **** */ static struct {int len; point_type point[10]; int transform_len, transform_list[8], max_white;} dekomino1 = {10, { {1,-1}, {1,0}, {0,1}, {1,1}, {2,1}, {3,1}, {0,2}, {1,2}, {2,2}, {3,2}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 5}; static int set_dekomino_puzzle1(polyominoesstruct *sp) { int perm_point[10], perm_transform[8], i, p; sp->width = 32; sp->height =30; sp->nr_polyominoes = 96; set_allocate(sp->polyomino,polyomino_type,96*sizeof(polyomino_type)); for (p=0;p<96;p++) { copy_polyomino(sp->polyomino[p],dekomino1,1); } sp->check_ok = check_dekomino_puzzle; return 1; } /* * *** fills 96 x 26 rectangle **** */ static struct {int len; point_type point[10]; int transform_len, transform_list[8], max_white;} octomino1 = {8, { {1,0}, {0,1}, {1,1}, {2,1}, {0,2}, {1,2}, {2,2}, {3,2}}, 8, {0, 1, 2, 3, 4, 5, 6, 7}, 5}; static int set_octomino_puzzle1(polyominoesstruct *sp) { int perm_point[8], perm_transform[8], i, p; sp->width = 96; sp->height =26; sp->nr_polyominoes = 312; set_allocate(sp->polyomino,polyomino_type,312*sizeof(polyomino_type)); for (p=0;p<312;p++) { copy_polyomino(sp->polyomino[p],octomino1,1); } sp->check_ok = check_octomino_puzzle; return 1; } /* * fills 15 x 15 rectangle **** */ static int set_pentomino_puzzle2(polyominoesstruct *sp) { int perm_point[5], perm_transform[8], i, p; sp->width = 15; sp->height =15; sp->nr_polyominoes = 45; set_allocate(sp->polyomino,polyomino_type,45*sizeof(polyomino_type)); for (p=0;p<45;p++) { copy_polyomino(sp->polyomino[p],pentomino1,1); } sp->check_ok = check_pentomino_puzzle; return 1; } /* *** **** fills a 47x33 rectangle **** */ static int set_elevenomino_puzzle2(polyominoesstruct *sp) { int perm_point[11], perm_transform[8], i, p; sp->width = 47; sp->height =33; sp->nr_polyominoes = 141; set_allocate(sp->polyomino,polyomino_type,141*sizeof(polyomino_type)); for (p=0;p<141;p++) { copy_polyomino(sp->polyomino[p],elevenomino1,1); } sp->check_ok = check_elevenomino_puzzle; return 1; } /************************************************** The main functions. **************************************************/ #define allocate(p,type,size) p = (type *) malloc(size); if ((p)==NULL) {free_polyominoes(sp); return;} void init_polyominoes(ModeInfo * mi) { polyominoesstruct *sp; int i,x,y, start; int box1, box2; int *perm; if (polyominoeses == NULL) { if ((polyominoeses = (polyominoesstruct *) calloc(MI_NUM_SCREENS(mi),sizeof (polyominoesstruct))) == NULL) return; } sp = &polyominoeses[MI_SCREEN(mi)]; free_polyominoes(sp); sp->rot180 = 0; sp->counter = 0; if (MI_IS_FULLRANDOM(mi)) { sp->identical = (Bool) (LRAND() & 1); sp->use3D = (Bool) (NRAND(4)); } else { sp->identical = identical; sp->use3D = !plain; } if (sp->identical) { switch (NRAND(9)) { case 0: if (!set_pentomino_puzzle1(sp)) return; break; case 1: if (!set_hexomino_puzzle1(sp)) return; break; case 2: if (!set_heptomino_puzzle1(sp)) return; break; case 3: if (!set_heptomino_puzzle2(sp)) return; break; case 4: if (!set_elevenomino_puzzle1(sp)) return; break; case 5: if (!set_dekomino_puzzle1(sp)) return; break; case 6: if (!set_octomino_puzzle1(sp)) return; break; case 7: if (!set_pentomino_puzzle2(sp)) return; break; case 8: if (!set_elevenomino_puzzle2(sp)) return; break; } } else { switch (NRAND(5)) { case 0: if (!set_pentomino_puzzle(sp)) return; break; case 1: if (!set_one_sided_pentomino_puzzle(sp)) return; break; case 2: if (!set_one_sided_hexomino_puzzle(sp)) return; break; case 3: if (!set_pent_hexomino_puzzle(sp)) return; break; case 4: if (!set_tetr_pentomino_puzzle(sp)) return; break; } } allocate(sp->attach_list,int,sp->nr_polyominoes*sizeof(int)); sp->nr_attached = 0; if (sp->identical) { allocate(sp->reason_to_not_attach,int,sp->nr_polyominoes*sp->nr_polyominoes*sizeof(int)); } allocate(sp->array,int,sp->width*sp->height*sizeof(int)); allocate(sp->changed_array,int,sp->width*sp->height*sizeof(int)); for (x=0;xwidth;x++) for (y=0;yheight;y++) ARRAY(x,y) = -1; sp->left_right = NRAND(2); sp->top_bottom = NRAND(2); box1 = MI_WIDTH(mi)/(sp->width+2); box2 = MI_HEIGHT(mi)/(sp->height+2); if (box1box = box1; else sp->box = box2; if (sp->box >= 12) { sp->box = (sp->box/12)*12; create_bitmaps(mi,sp); if (!sp->use_bitmaps) free_bitmaps(sp); } else sp->use_bitmaps = 0; if (!sp->use_bitmaps) { allocate(sp->rectangles,XRectangle,sp->width*sp->height*sizeof(XRectangle)); allocate(sp->lines,XSegment,sp->width*sp->height*sizeof(XSegment)); } allocate(perm,int,sp->nr_polyominoes*sizeof(int)); random_permutation(sp->nr_polyominoes, perm); sp->mono = MI_NPIXELS(mi) < 12; start = NRAND(MI_NPIXELS(mi)); for (i=0;inr_polyominoes;i++) if (!sp->mono) { sp->polyomino[i].color = MI_PIXEL(mi,(perm[i]*MI_NPIXELS(mi) / sp->nr_polyominoes + start) % MI_NPIXELS(mi)); if (sp->rot180) { sp->polyomino[i+1].color = sp->polyomino[i].color; i++; } } else if(sp->use_bitmaps) sp->polyomino[i].color = MI_WHITE_PIXEL(mi); else sp->polyomino[i].color = MI_BLACK_PIXEL(mi); free(perm); if (sp->use_bitmaps) { if (sp->mono) sp->border_color = MI_WHITE_PIXEL(mi); else sp->border_color = MI_PIXEL(mi,NRAND(MI_NPIXELS(mi))); } sp->x_margin = (MI_WIDTH(mi)-sp->box*sp->width)/2; sp->y_margin = (MI_HEIGHT(mi)-sp->box*sp->height)/2; sp->wait = 0; /* Clear the background. */ MI_CLEARWINDOW(mi); } void draw_polyominoes(ModeInfo * mi) { polyominoesstruct *sp; int poly_no,point_no,transform_index,done,another_attachment_try; point_type attach_point; int i,detach_until; if (polyominoeses == NULL) return; sp = &polyominoeses[MI_SCREEN(mi)]; if (MI_CYCLES(mi) != 0) { if (++sp->counter > MI_CYCLES(mi)) { init_polyominoes(mi); return; } } if (sp->box == 0) { init_polyominoes(mi); return; } MI_IS_DRAWN(mi) = True; sp->wait--; if (sp->wait>0) return; (void) memset(sp->changed_array,0,sp->width*sp->height*sizeof(int)); poly_no = first_poly_no(sp); point_no = 0; transform_index = 0; done = 0; another_attachment_try = 1; find_blank(sp,&attach_point); if (sp->identical && sp->nr_attached < sp->nr_polyominoes) (void) memset(&REASON_TO_NOT_ATTACH(sp->nr_attached,0),0,sp->nr_polyominoes*sizeof(int)); while(!done) { if (sp->nr_attached < sp->nr_polyominoes) { while (!done && another_attachment_try) { done = attach(sp,poly_no,point_no,transform_index,attach_point,0,&REASON_TO_NOT_ATTACH(sp->nr_attached,0)); if (done && sp->rot180) { poly_no = first_poly_no(sp); done = attach(sp,poly_no,point_no,transform_index,attach_point,1,&REASON_TO_NOT_ATTACH(sp->nr_attached-1,0)); if (!done) detach(sp,&poly_no,&point_no,&transform_index,&attach_point,0); } if (!done) another_attachment_try = next_attach_try(sp,&poly_no,&point_no,&transform_index); } } if (sp->identical) { if (!done) { if (sp->nr_attached == 0) done = 1; else { detach_until=sp->nr_attached-1; if (sp->nr_attached < sp->nr_polyominoes) while (detach_until>0 && REASON_TO_NOT_ATTACH(sp->nr_attached,detach_until)==0) detach_until--; while (sp->nr_attached>detach_until) { if (sp->rot180) detach(sp,&poly_no,&point_no,&transform_index,&attach_point,1); detach(sp,&poly_no,&point_no,&transform_index,&attach_point,0); if (sp->nr_attached+1+sp->rot180 < sp->nr_polyominoes) for (i=0;inr_polyominoes;i++) REASON_TO_NOT_ATTACH(sp->nr_attached,i) |= REASON_TO_NOT_ATTACH(sp->nr_attached+1+sp->rot180,i); } another_attachment_try = next_attach_try(sp,&poly_no,&point_no,&transform_index); } } } else { if (!done) { if (sp->nr_attached == 0) done = 1; else { if (sp->rot180) detach(sp,&poly_no,&point_no,&transform_index,&attach_point,1); detach(sp,&poly_no,&point_no,&transform_index,&attach_point,0); } another_attachment_try = next_attach_try(sp,&poly_no,&point_no,&transform_index); } } } if (sp->use_bitmaps) draw_with_bitmaps(mi,sp); else draw_without_bitmaps(mi,sp); if (sp->nr_attached == sp->nr_polyominoes) sp->wait = 500; else sp->wait = 0; } void release_polyominoes(ModeInfo * mi) { int screen; if (polyominoeses != NULL) { for (screen=0;screen