|
@@ -0,0 +1,887 @@
|
|
|
|
+program meteorshower;
|
|
|
|
+
|
|
|
|
+{Shootout Meteor puzzle implementation
|
|
|
|
+
|
|
|
|
+ by Daniel Mantione
|
|
|
|
+
|
|
|
|
+ mostly based on Ben St. John's implementation.}
|
|
|
|
+
|
|
|
|
+{$Q-}
|
|
|
|
+
|
|
|
|
+uses dos;
|
|
|
|
+
|
|
|
|
+const N_COL = 5;
|
|
|
|
+ N_ROW = 10;
|
|
|
|
+ N_CELL = N_COL * N_ROW;
|
|
|
|
+ N_PIECE_TYPE = 10;
|
|
|
|
+
|
|
|
|
+ N_ELEM=5;
|
|
|
|
+ N_ORIENT=12;
|
|
|
|
+ ALL_PIECE_MASK=[0..N_PIECE_TYPE-1];
|
|
|
|
+ SKIP_PIECE=5;
|
|
|
|
+
|
|
|
|
+ no_piece=high(byte);
|
|
|
|
+
|
|
|
|
+ L_EDGE_MASK=[0,5,10,15,20,25,30];
|
|
|
|
+ R_EDGE_MASK=[4,9,14,19,24,29];
|
|
|
|
+ TOP_ROW = [0*N_COL..1*N_COL-1];
|
|
|
|
+ SECOND_ROW = [1*N_COL..2*N_COL-1];
|
|
|
|
+ THIRD_ROW = [2*N_COL..3*N_COL-1];
|
|
|
|
+ FOURTH_ROW = [3*N_COL..4*N_COL-1];
|
|
|
|
+ FIFTH_ROW = [4*N_COL..5*N_COL-1];
|
|
|
|
+ SIXTH_ROW = [5*N_COL..6*N_COL-1];
|
|
|
|
+ LAST_ROW = SIXTH_ROW;
|
|
|
|
+ ROW_0_MASK=[0..N_COL-1,10..N_COL+10-1,20..N_COL+20-1,30,31];
|
|
|
|
+ ROW_1_MASK=[5..N_COL+5-1,15..N_COL+15-1,25..N_COL+25-1];
|
|
|
|
+ BOARD_MASK=[0..29];
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+type bitvec=set of 0..31;
|
|
|
|
+ dimensions=(dimx,dimy);
|
|
|
|
+ parity=(even,odd);
|
|
|
|
+ goodbad=(good,bad,always_bad);
|
|
|
|
+ piecenr=type 0..N_PIECE_TYPE-1;
|
|
|
|
+ orientation=type 0..N_ORIENT-1;
|
|
|
|
+
|
|
|
|
+ piece_placement=record
|
|
|
|
+ vec:bitvec;
|
|
|
|
+ ipiece:piecenr;
|
|
|
|
+ row:byte;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+type Soln=object
|
|
|
|
+ m_pieces:array[piecenr] of piece_placement;
|
|
|
|
+ m_npiece:byte;
|
|
|
|
+ m_cells:array[0..N_ROW-1,0..N_COL-1] of piecenr;
|
|
|
|
+ m_synched:boolean;
|
|
|
|
+ constructor init(fillval:byte);
|
|
|
|
+ procedure setCells;
|
|
|
|
+ function lessThan(var r:Soln):boolean;
|
|
|
|
+ procedure write(var f:text);
|
|
|
|
+ procedure fill(value:byte);
|
|
|
|
+ procedure spin(var spun:Soln);
|
|
|
|
+
|
|
|
|
+ function isEmpty:boolean;
|
|
|
|
+ procedure popPiece;inline;
|
|
|
|
+ procedure pushPiece(Avec:bitvec;AiPiece:piecenr;Arow:byte);
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ instance=record
|
|
|
|
+ m_allowed:set of byte;
|
|
|
|
+ m_vec:bitvec;
|
|
|
|
+ m_offset:longint;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ TPts=array[0..N_ELEM-1,dimensions] of shortint;
|
|
|
|
+ piece=object
|
|
|
|
+ m_instance:array[parity] of instance;
|
|
|
|
+ procedure set_ok_positions(isOdd:parity;w,h:longint);
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ OkPieces=record
|
|
|
|
+ nPieces:array[piecenr] of byte;
|
|
|
|
+ pieceVec:array[piecenr,orientation] of bitvec;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+type fixed=(OPEN, CLOSED);
|
|
|
|
+ islandinfo=record
|
|
|
|
+ has_bad:array[fixed,parity] of bitvec;
|
|
|
|
+ is_known:array[fixed,parity] of bitvec;
|
|
|
|
+ alwaysBad:array[parity] of bitvec;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+const MAX_ISLAND_OFFSET=1024;
|
|
|
|
+
|
|
|
|
+type cacherec=record
|
|
|
|
+ krow,kpiecevec:word;
|
|
|
|
+ kboardvec:bitvec;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+var s_basePiece:array[piecenr,orientation] of piece;
|
|
|
|
+ g_okPieces:array[0..N_ROW-1,0..N_COL-1] of OkPieces;
|
|
|
|
+ g_islandInfo:array[0..MAX_ISLAND_OFFSET-1] of islandinfo;
|
|
|
|
+ g_nIslandInfo:cardinal=0;
|
|
|
|
+ cache:array[0..1024*128-1] of cacherec;
|
|
|
|
+
|
|
|
|
+ m_curSoln,m_minSoln,m_maxSoln:Soln;
|
|
|
|
+ m_nSoln:cardinal;
|
|
|
|
+
|
|
|
|
+const basevecs:array [0..9] of bitvec= (
|
|
|
|
+ [0,1,2,3,8],
|
|
|
|
+ [0,1,3,6,7],
|
|
|
|
+ [0,1,2,7,12],
|
|
|
|
+ [0,1,2,5,10],
|
|
|
|
+ [0,2,5,6,10],
|
|
|
|
+ [0,1,2,6,7],
|
|
|
|
+ [0,1,5,10,15],
|
|
|
|
+ [0,1,2,5,7],
|
|
|
|
+ [0,1,2,7,8],
|
|
|
|
+ [0,1,2,3,7]
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+constructor soln.init(fillval:byte);
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ fill(fillval);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure Soln.fill(value:byte);
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ m_synched:=false;
|
|
|
|
+ fillchar(m_cells,N_CELL,value);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function soln.isEmpty:boolean;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ isempty:=m_nPiece=0;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure soln.pushPiece(Avec:bitvec;AiPiece:piecenr;Arow:byte);
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ with m_pieces[m_npiece] do
|
|
|
|
+ begin
|
|
|
|
+ vec:=Avec;
|
|
|
|
+ iPiece:=AiPiece;
|
|
|
|
+ row:=Arow;
|
|
|
|
+ end;
|
|
|
|
+ inc(m_npiece);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure soln.popPiece;inline;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ dec(m_nPiece);
|
|
|
|
+ m_synched := false;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure soln.write(var f:text);
|
|
|
|
+
|
|
|
|
+var x,y:byte;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ for y:=0 to N_ROW-1 do
|
|
|
|
+ begin
|
|
|
|
+ {indent every second line}
|
|
|
|
+ if y mod 2=1 then
|
|
|
|
+ system.write(f,' ');
|
|
|
|
+ for x:=0 to N_COL-1 do
|
|
|
|
+ if m_cells[y,x]=no_piece then
|
|
|
|
+ system.write(f,'. ')
|
|
|
|
+ else
|
|
|
|
+ system.write(f,char(byte('0')+m_cells[y,x]),' ');
|
|
|
|
+ writeln(f);
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+procedure Soln.setCells;
|
|
|
|
+
|
|
|
|
+var c,i,x,y,newcells:byte;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ if m_synched then
|
|
|
|
+ exit;
|
|
|
|
+ for i:=1 to m_nPiece do
|
|
|
|
+ with m_pieces[i-1] do
|
|
|
|
+ begin
|
|
|
|
+ newcells:=0;
|
|
|
|
+ c:=0;
|
|
|
|
+ for y:=row to N_ROW do
|
|
|
|
+ begin
|
|
|
|
+ for x:=0 to N_COL-1 do
|
|
|
|
+ begin
|
|
|
|
+ if c in vec then
|
|
|
|
+ begin
|
|
|
|
+ m_cells[y,x]:=ipiece;
|
|
|
|
+ inc(NewCells);
|
|
|
|
+ end;
|
|
|
|
+ inc(c);
|
|
|
|
+ end;
|
|
|
|
+ if NewCells=N_ELEM then
|
|
|
|
+ break;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ m_synched:=true;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function Soln.lessThan(var r:Soln):boolean;
|
|
|
|
+
|
|
|
|
+var x,y,lval,rval:byte;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ if m_pieces[0].iPiece<>r.m_pieces[0].iPiece then
|
|
|
|
+ begin
|
|
|
|
+ lessthan:=m_pieces[0].iPiece < r.m_pieces[0].iPiece;
|
|
|
|
+ exit;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ setCells();
|
|
|
|
+ r.setCells();
|
|
|
|
+
|
|
|
|
+ for y:=0 to N_ROW-1 do
|
|
|
|
+ for x:=0 to N_COL-1 do
|
|
|
|
+ begin
|
|
|
|
+ lval:=m_cells[y,x];
|
|
|
|
+ rval:=r.m_cells[y,x];
|
|
|
|
+
|
|
|
|
+ if lval <> rval then
|
|
|
|
+ begin
|
|
|
|
+ lessthan:=lval<rval;
|
|
|
|
+ exit;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ lessthan:=false; {solutions are equal}
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure Soln.spin(var spun:Soln);
|
|
|
|
+
|
|
|
|
+var x,y:byte;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ setCells;
|
|
|
|
+ {swap cells}
|
|
|
|
+ for y:=0 to N_ROW-1 do
|
|
|
|
+ for x:=0 to N_COL-1 do
|
|
|
|
+ spun.m_cells[y,x]:=m_cells[N_ROW-y-1,N_COL-x-1];
|
|
|
|
+
|
|
|
|
+ {swap first and last pieces (the rest aren't used)}
|
|
|
|
+ spun.m_pieces[0].iPiece:=m_pieces[N_PIECE_TYPE-1].iPiece;
|
|
|
|
+ spun.m_synched:=true;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function floor(top,bot:longint):longint;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ floor:=top div bot;
|
|
|
|
+ {negative numbers should be rounded down, not towards zero}
|
|
|
|
+ if (floor*bot<>top) and ((top<0) xor (bot<=0)) then
|
|
|
|
+ dec(floor);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+const s_firstOne:array[0..31] of byte=(
|
|
|
|
+ 0, 0, 1, 0, 2, 0, 1, 0,
|
|
|
|
+ 3, 0, 1, 0, 2, 0, 1, 0,
|
|
|
|
+
|
|
|
|
+ 4, 0, 1, 0, 2, 0, 1, 0,
|
|
|
|
+ 3, 0, 1, 0, 2, 0, 1, 0
|
|
|
|
+);
|
|
|
|
+
|
|
|
|
+function first_set_bit(v:bitvec):cardinal;inline;
|
|
|
|
+
|
|
|
|
+{$ifdef endian_little}
|
|
|
|
+const l=0;
|
|
|
|
+ h=1;
|
|
|
|
+{$else}
|
|
|
|
+const l=1;
|
|
|
|
+ h=0;
|
|
|
|
+{$endif}
|
|
|
|
+
|
|
|
|
+var d:double;
|
|
|
|
+ u:array[0..1] of bitvec absolute d;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ first_set_bit:=0;
|
|
|
|
+ if v<>[] then
|
|
|
|
+ begin
|
|
|
|
+ u[l]:=v;
|
|
|
|
+ u[h]:=[30,25,24,21,20];
|
|
|
|
+ d:=d-4503599627370496;
|
|
|
|
+ first_set_bit:=cardinal(u[h]) shr 20-$3ff;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function count_ones(v:bitvec):cardinal;inline;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ count_ones:=0;
|
|
|
|
+ while v<>[] do
|
|
|
|
+ begin
|
|
|
|
+ inc(count_ones);
|
|
|
|
+ cardinal(v):=cardinal(v) and (cardinal(v)-1);
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure setCoordList(vec:bitvec;var pts:Tpts);
|
|
|
|
+
|
|
|
|
+var iPt,n:longint;
|
|
|
|
+ x,y:byte;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ iPt:=0;
|
|
|
|
+ n:=0;
|
|
|
|
+ for y:=0 to N_ROW-1 do
|
|
|
|
+ for x:=0 to N_COL-1 do
|
|
|
|
+ begin
|
|
|
|
+ if n in vec then
|
|
|
|
+ begin
|
|
|
|
+ pts[iPt,dimx]:=x;
|
|
|
|
+ pts[iPt,dimy]:=y;
|
|
|
|
+ inc(iPt);
|
|
|
|
+ end;
|
|
|
|
+ inc(n);
|
|
|
|
+ if n=32 then
|
|
|
|
+ exit;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function toBitVector(const pts:Tpts):bitvec;
|
|
|
|
+
|
|
|
|
+var x,y,iPt:byte;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ tobitvector:=[];
|
|
|
|
+ for iPt:=low(pts) to high(pts) do
|
|
|
|
+ begin
|
|
|
|
+ x:=pts[iPt,dimx];
|
|
|
|
+ y:=pts[iPt,dimy];
|
|
|
|
+ include(tobitvector,y*N_COL+x);
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure shiftUpLines(var pts:Tpts;shift:longint);
|
|
|
|
+
|
|
|
|
+var iPt:byte;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ {vertical shifts have a twist}
|
|
|
|
+ for iPt:=low(pts) to high(pts) do
|
|
|
|
+ begin
|
|
|
|
+ if pts[iPt,dimy] and shift and 1<>0 then
|
|
|
|
+ inc(pts[iPt,dimx]);
|
|
|
|
+ dec(pts[iPt,dimy],shift);
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function shiftToX0(var pts:Tpts;var Ainstance:instance;offsetRow:longint):shortint;
|
|
|
|
+
|
|
|
|
+var x,y,xmin,xmax,iPt,offset:shortint;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ { .. determine shift}
|
|
|
|
+ xMin:=pts[0,dimx];
|
|
|
|
+ xMax:=xMin;
|
|
|
|
+ for iPt:=low(pts)+1 to high(pts) do
|
|
|
|
+ begin
|
|
|
|
+ x:=pts[iPt,dimx];
|
|
|
|
+ y:=pts[iPt,dimy];
|
|
|
|
+ if x<xMin then
|
|
|
|
+ xMin:=x
|
|
|
|
+ else if x > xMax then
|
|
|
|
+ xMax:=x;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ offset:=N_ELEM;
|
|
|
|
+ for iPt:=low(pts) to high(pts) do
|
|
|
|
+ begin
|
|
|
|
+ dec(pts[iPt,dimx],xMin);
|
|
|
|
+ {check offset -- leftmost cell on top line}
|
|
|
|
+ if (pts[iPt,dimy]=offsetRow) and (pts[iPt,dimx]<offset) then
|
|
|
|
+ offset:=pts[iPt,dimx];
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ Ainstance.m_offset := offset;
|
|
|
|
+ Ainstance.m_vec := toBitVector(pts);
|
|
|
|
+ shifttox0:=xMax - xMin;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function badregion(var to_fill:bitvec;rnew:bitvec):boolean;
|
|
|
|
+
|
|
|
|
+var region,even_region,odd_region:bitvec;
|
|
|
|
+ cell_count:cardinal;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ {Grow empty region, until it doesn't change any more.}
|
|
|
|
+ repeat
|
|
|
|
+ region:=rnew;
|
|
|
|
+ even_region:=region*(ROW_0_MASK*([0..31]-L_EDGE_MASK));
|
|
|
|
+ odd_region:=region*(ROW_1_MASK*([0..31]-R_EDGE_MASK));
|
|
|
|
+
|
|
|
|
+ rnew:=to_fill*(rnew
|
|
|
|
+ {simple grow up/down}
|
|
|
|
+ +bitvec(cardinal(region) shr N_COL)
|
|
|
|
+ +bitvec(cardinal(region) shl N_COL)
|
|
|
|
+ {grow right/left}
|
|
|
|
+ +bitvec(cardinal(region) and not cardinal(L_EDGE_MASK) shr 1)
|
|
|
|
+ +bitvec(cardinal(region) and not cardinal(R_EDGE_MASK) shl 1)
|
|
|
|
+ {tricky growth}
|
|
|
|
+ +bitvec(cardinal(even_Region) shr (N_COL+1))
|
|
|
|
+ +bitvec(cardinal(even_Region) shl (N_COL-1))
|
|
|
|
+ +bitvec(cardinal(odd_Region) shr (N_COL-1))
|
|
|
|
+ +bitvec(cardinal(odd_Region) shl (N_COL+1))
|
|
|
|
+ );
|
|
|
|
+ until (rnew=to_fill) or (rnew=region);
|
|
|
|
+
|
|
|
|
+ {Subtract empty region from board.}
|
|
|
|
+ to_fill:=to_fill-rnew;
|
|
|
|
+
|
|
|
|
+ cell_count:=count_ones(to_fill);
|
|
|
|
+ {Optimize 'cell_count mod 5<>0' by hand...}
|
|
|
|
+ badregion:=cell_count<>((cell_count*$cccd) shr 18)*5;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function has_bad_islands_single(boardVec:bitvec;row:longint):boolean;
|
|
|
|
+
|
|
|
|
+var tofill,startregion,bmask:bitvec;
|
|
|
|
+ isodd:boolean;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ tofill:=[0..31]-boardvec;
|
|
|
|
+ isOdd:=row and 1<>0;
|
|
|
|
+ if isOdd then
|
|
|
|
+ begin
|
|
|
|
+ dec(row);
|
|
|
|
+ toFill:=bitvec(cardinal(tofill) shl N_COL); {shift to even aligned}
|
|
|
|
+ toFill:= tofill + TOP_ROW;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ startRegion := TOP_ROW;
|
|
|
|
+ bMask := BOARD_MASK; {all but the first two bits}
|
|
|
|
+ if row>=4 then
|
|
|
|
+ cardinal(bMask):=cardinal(bmask) shr ((row-4)*N_COL)
|
|
|
|
+ else if isOdd or (row = 0) then
|
|
|
|
+ startRegion := LAST_ROW;
|
|
|
|
+
|
|
|
|
+ toFill:=tofill*bMask;
|
|
|
|
+ startRegion:=startregion*toFill;
|
|
|
|
+
|
|
|
|
+ has_bad_islands_single:=true;
|
|
|
|
+ while toFill<>[] do
|
|
|
|
+ begin
|
|
|
|
+ if badRegion(toFill, startRegion) then
|
|
|
|
+ exit;
|
|
|
|
+ startRegion:=[first_set_bit(toFill)];
|
|
|
|
+ end;
|
|
|
|
+ has_bad_islands_single:=false;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+procedure piece.set_ok_positions(isOdd:parity;w,h:longint);
|
|
|
|
+
|
|
|
|
+var x,y,xpos,pos:byte;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ pos:=byte(isodd)*N_COL;
|
|
|
|
+ with m_instance[isOdd] do
|
|
|
|
+ begin
|
|
|
|
+ m_allowed:=[];
|
|
|
|
+ y:=byte(isOdd);
|
|
|
|
+ while y<N_ROW-h do
|
|
|
|
+ begin
|
|
|
|
+ if m_offset<>0 then
|
|
|
|
+ inc(pos,m_offset);
|
|
|
|
+ for xPos:=0 to N_COL-1-m_offset do
|
|
|
|
+ begin
|
|
|
|
+ {check if the new position is on the board}
|
|
|
|
+ if (xPos<N_COL-w) and not has_bad_islands_single(bitvec(cardinal(m_vec) shl xPos),y) then
|
|
|
|
+ begin
|
|
|
|
+ {position is allowed}
|
|
|
|
+ include(m_allowed,pos);
|
|
|
|
+ end;
|
|
|
|
+ inc(pos);
|
|
|
|
+ end;
|
|
|
|
+ y:=y+2;
|
|
|
|
+ {Skip row with wrong parity:}
|
|
|
|
+ inc(pos,N_COL);
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure gen_orientation(vec:bitvec;iOrient:cardinal;var target:Piece);
|
|
|
|
+
|
|
|
|
+var pts:Tpts;
|
|
|
|
+ x,y,ymin,ymax,h,w:shortint;
|
|
|
|
+ rot,iPt:byte;
|
|
|
|
+ flip:boolean;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ {get (x,y) coordinates}
|
|
|
|
+ setCoordList(vec, pts);
|
|
|
|
+
|
|
|
|
+ rot := iOrient mod 6;
|
|
|
|
+ flip := iOrient >= 6;
|
|
|
|
+ if flip then
|
|
|
|
+ for iPt:=0 to N_ELEM-1 do
|
|
|
|
+ pts[iPt,dimy]:=-pts[iPt,dimy];
|
|
|
|
+
|
|
|
|
+ {rotate as necessary}
|
|
|
|
+ while rot>0 do
|
|
|
|
+ begin
|
|
|
|
+ for iPt:=0 to N_ELEM-1 do
|
|
|
|
+ begin
|
|
|
|
+ x:=pts[iPt,dimx];
|
|
|
|
+ y:=pts[iPt,dimy];
|
|
|
|
+ pts[iPt,dimx]:=floor(2*x-3*y+1,4);
|
|
|
|
+ pts[iPt,dimy]:=floor(2*x+y+1,2);
|
|
|
|
+ end;
|
|
|
|
+ dec(rot);
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ {determine vertical shift}
|
|
|
|
+ yMin := pts[0,dimy];
|
|
|
|
+ yMax := yMin;
|
|
|
|
+ for iPt:= 1 to N_ELEM-1 do
|
|
|
|
+ begin
|
|
|
|
+ y := pts[iPt,dimy];
|
|
|
|
+
|
|
|
|
+ if y < yMin then
|
|
|
|
+ yMin := y
|
|
|
|
+ else if y > yMax then
|
|
|
|
+ yMax := y;
|
|
|
|
+ end;
|
|
|
|
+ h:=yMax-yMin;
|
|
|
|
+
|
|
|
|
+ shiftUpLines(pts, yMin);
|
|
|
|
+ w := shiftToX0(pts, target.m_instance[EVEN], 0);
|
|
|
|
+ target.set_ok_positions(EVEN, w, h);
|
|
|
|
+ cardinal(target.m_instance[EVEN].m_vec) := cardinal(target.m_instance[EVEN].m_vec) shr target.m_instance[EVEN].m_offset;
|
|
|
|
+
|
|
|
|
+ {shift down one line}
|
|
|
|
+ shiftUpLines(pts, -1);
|
|
|
|
+ w := shiftToX0(pts, target.m_instance[ODD], 1);
|
|
|
|
+ {shift the bitmask back one line}
|
|
|
|
+ cardinal(target.m_instance[ODD].m_vec) :=cardinal(target.m_instance[ODD].m_vec) shr N_COL;
|
|
|
|
+ target.set_ok_positions(ODD, w, h);
|
|
|
|
+ cardinal(target.m_instance[ODD].m_vec):= cardinal(target.m_instance[ODD].m_vec) shr target.m_instance[ODD].m_offset;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function getPiece(iPiece,iOrient:cardinal;iParity:parity):instance;inline;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ getpiece:=s_basePiece[iPiece][iOrient].m_instance[iParity];
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure gen_all_orientations;
|
|
|
|
+
|
|
|
|
+var ipiece:piecenr;
|
|
|
|
+ iorient:orientation;
|
|
|
|
+ irow,icol:byte;
|
|
|
|
+ refpiece:bitvec;
|
|
|
|
+ n,npiece:byte;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ for iPiece:=low(ipiece) to high(ipiece) do
|
|
|
|
+ begin
|
|
|
|
+ refPiece:=BaseVecs[iPiece];
|
|
|
|
+ for iOrient:=low(iorient) to high(iorient) do
|
|
|
|
+ begin
|
|
|
|
+ gen_orientation(refPiece, iOrient, s_basePiece[iPiece,iOrient]);
|
|
|
|
+ with s_basePiece[iPiece,iOrient] do
|
|
|
|
+ begin
|
|
|
|
+ if (iPiece=SKIP_PIECE) and (iOrient in [3..5,9..11]) then
|
|
|
|
+ begin
|
|
|
|
+ m_instance[odd].m_allowed := [];
|
|
|
|
+ m_instance[even].m_allowed := [];
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ for iPiece:=low(ipiece) to high(ipiece) do
|
|
|
|
+ begin
|
|
|
|
+ for iOrient:=low(iorient) to high(iorient) do
|
|
|
|
+ begin
|
|
|
|
+ n:=0;
|
|
|
|
+ for iRow:=0 to N_ROW-1 do
|
|
|
|
+ begin
|
|
|
|
+ with getPiece(iPiece, iOrient, parity(iRow and 1)) do
|
|
|
|
+ for iCol:=0 to N_COL-1 do
|
|
|
|
+ begin
|
|
|
|
+ if n in m_allowed then
|
|
|
|
+ begin
|
|
|
|
+ nPiece:=g_okPieces[iRow,iCol].nPieces[iPiece];
|
|
|
|
+ g_okPieces[iRow,iCol].pieceVec[iPiece,nPiece]:=bitvec(cardinal(m_vec) shl iCol);
|
|
|
|
+ inc(g_okPieces[iRow,iCol].nPieces[iPiece]);
|
|
|
|
+ end;
|
|
|
|
+ inc(n);
|
|
|
|
+ end;
|
|
|
|
+ end
|
|
|
|
+ end
|
|
|
|
+ end
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure init_board;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ m_cursoln.init(NO_PIECE);
|
|
|
|
+ m_minsoln.init(NO_PIECE);
|
|
|
|
+ m_maxsoln.init(NO_PIECE);
|
|
|
|
+ m_nsoln:=0;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+const g_firstRegion:array[0..31] of bitvec=(
|
|
|
|
+ [], [0], [1], [0,1],
|
|
|
|
+ [2], [0], [1,2], [0,1,2],
|
|
|
|
+ [3], [0], [1], [0,1],
|
|
|
|
+ [2,3], [0], [1,2,3], [0,1,2,3],
|
|
|
|
+ [4], [0], [1], [0,1],
|
|
|
|
+ [2], [0], [1,2], [0,1,2],
|
|
|
|
+ [3,4], [0], [1], [1,2],
|
|
|
|
+ [2,3,4], [0], [1,2,3,4], [0,1,2,3,4]
|
|
|
|
+);
|
|
|
|
+
|
|
|
|
+function calc_bad_islands(boardVec:bitvec;row:longint):goodbad;
|
|
|
|
+
|
|
|
|
+var tofill,boardmask,bottom,startregion:bitvec;
|
|
|
|
+ filled:boolean;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ toFill:=[0..31]-boardVec;
|
|
|
|
+ {Compensate for odd rows.}
|
|
|
|
+ if row and 1<>0 then
|
|
|
|
+ begin
|
|
|
|
+ dec(row);
|
|
|
|
+ cardinal(toFill):=cardinal(tofill) shl N_COL;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ boardMask := BOARD_MASK; {all but the first two bits}
|
|
|
|
+ if row>4 then
|
|
|
|
+ cardinal(boardMask):=cardinal(boardmask) shr ((row-4)*N_COL);
|
|
|
|
+ toFill:=tofill*boardMask;
|
|
|
|
+
|
|
|
|
+ {a little pre-work to speed things up}
|
|
|
|
+ filled:=toFill*LAST_ROW=LAST_ROW;
|
|
|
|
+ bottom:=LAST_ROW;
|
|
|
|
+ while bottom*toFill=bottom do
|
|
|
|
+ begin
|
|
|
|
+ toFill:=tofill-bottom;
|
|
|
|
+ cardinal(bottom):=cardinal(bottom) shr N_COL;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ if filled or (row<4) then
|
|
|
|
+ startRegion := bottom * toFill
|
|
|
|
+ else
|
|
|
|
+ begin
|
|
|
|
+ startRegion := g_firstRegion[cardinal(toFill*TOP_ROW)];
|
|
|
|
+ if startRegion=[] then
|
|
|
|
+ begin
|
|
|
|
+ startRegion := bitvec(cardinal(toFill) shr N_COL)*TOP_ROW;
|
|
|
|
+ startRegion := g_firstRegion[cardinal(startRegion)];
|
|
|
|
+ cardinal(startRegion) := cardinal(startregion) shl N_COL;
|
|
|
|
+ end;
|
|
|
|
+ startRegion:=startregion+bitvec(cardinal(startRegion) shl N_COL)*toFill;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ while toFill<>[] do
|
|
|
|
+ begin
|
|
|
|
+ if badRegion(toFill, startRegion) then
|
|
|
|
+ begin
|
|
|
|
+ if toFill<>[] then
|
|
|
|
+ calc_bad_islands:=ALWAYS_BAD
|
|
|
|
+ else
|
|
|
|
+ calc_bad_islands:=BAD;
|
|
|
|
+ exit;
|
|
|
|
+ end;
|
|
|
|
+ startRegion := [first_set_bit(toFill)];
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ calc_bad_islands:=GOOD;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+function has_bad_islands(boardvec:bitvec;row:longint):goodbad;
|
|
|
|
+
|
|
|
|
+var last_row:bitvec;
|
|
|
|
+ isodd:parity;
|
|
|
|
+ isclosed:fixed;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ {skip over any filled rows}
|
|
|
|
+ while boardVec*TOP_ROW=TOP_ROW do
|
|
|
|
+ begin
|
|
|
|
+ cardinal(boardVec):=cardinal(boardvec) shr N_COL;
|
|
|
|
+ inc(row);
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ has_bad_islands:=bad;
|
|
|
|
+ with g_islandInfo[cardinal(boardvec*(TOP_ROW+SECOND_ROW))] do
|
|
|
|
+ begin
|
|
|
|
+ last_row:=bitvec(cardinal(boardvec) shr (2*N_COL))*TOP_ROW;
|
|
|
|
+ isOdd:=parity(row and 1);
|
|
|
|
+
|
|
|
|
+ if not(cardinal(last_row) in alwaysBad[parity(row and 1)]) then
|
|
|
|
+ if boardVec*bitvec(cardinal(TOP_ROW) shl N_COL*3)=[] then
|
|
|
|
+ begin
|
|
|
|
+ isClosed:=fixed(row>6); {because we track 3 rows}
|
|
|
|
+ if not(cardinal(last_row) in is_known[isClosed,isOdd]) then
|
|
|
|
+ if boardVec<>[] then
|
|
|
|
+ begin
|
|
|
|
+ has_bad_islands:=calc_bad_islands(boardvec,row);
|
|
|
|
+ include(is_known[isClosed,isOdd],cardinal(last_row));
|
|
|
|
+ if has_bad_islands<>good then
|
|
|
|
+ include(is_known[isClosed,isOdd],cardinal(last_row));
|
|
|
|
+ end
|
|
|
|
+ else
|
|
|
|
+ has_bad_islands:=good
|
|
|
|
+ else
|
|
|
|
+ if not(cardinal(last_row) in has_bad[isClosed,isOdd]) then
|
|
|
|
+ has_bad_islands:=good;
|
|
|
|
+ end
|
|
|
|
+ else
|
|
|
|
+ has_bad_islands:=calc_bad_islands(boardvec,row);
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+const g_flip:array[0..31] of bitvec=(
|
|
|
|
+ [], [4], [3], [3,4],
|
|
|
|
+ [2], [2,4], [2,3], [2,3,4],
|
|
|
|
+ [1], [1,4], [1,3], [1,3,4],
|
|
|
|
+ [1,2], [1,2,4], [1,2,3], [1,2,3,4],
|
|
|
|
+ [0], [0,4], [0,3], [0,3,4],
|
|
|
|
+ [0,2], [0,2,4], [0,2,3], [0,2,3,4],
|
|
|
|
+ [0,1], [0,1,4], [0,1,3], [0,1,3,4],
|
|
|
|
+ [0,1,2], [0,1,2,4], [0,1,2,3], [0,1,2,3,4]
|
|
|
|
+);
|
|
|
|
+
|
|
|
|
+function flipTwoRows(bits:bitvec):bitvec;inline;
|
|
|
|
+
|
|
|
|
+var flipped:cardinal;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ flipped:=cardinal(g_flip[cardinal(bits) shr N_COL]) shl N_COL;
|
|
|
|
+ fliptworows:=bitvec(flipped or cardinal(g_flip[cardinal(bits*TOP_ROW)]));
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure mark_bad(var info:IslandInfo;n:byte;eo:parity;always:boolean);inline;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ with info do
|
|
|
|
+ begin
|
|
|
|
+ include(has_bad[OPEN,eo],n);
|
|
|
|
+ include(has_bad[CLOSED,eo],n);
|
|
|
|
+
|
|
|
|
+ if always then
|
|
|
|
+ include(alwaysBad[eo],n);
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure calc_always_bad;
|
|
|
|
+
|
|
|
|
+var i,iWord:cardinal;
|
|
|
|
+ boardvec:bitvec;
|
|
|
|
+ hasbad:goodbad;
|
|
|
|
+ always:boolean;
|
|
|
|
+ flipped:^islandinfo;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ for iWord:=1 to MAX_ISLAND_OFFSET-1 do
|
|
|
|
+ begin
|
|
|
|
+ flipped := @g_islandInfo[cardinal(flipTwoRows(bitvec(iWord)))];
|
|
|
|
+ for i:=0 to 31 do
|
|
|
|
+ begin
|
|
|
|
+ boardvec:=bitvec((i shl (2*N_COL)) or iWord);
|
|
|
|
+ if not(i in g_islandInfo[iWord].is_known[OPEN,EVEN]) then
|
|
|
|
+ begin
|
|
|
|
+ hasBad:=calc_bad_islands(boardvec,0);
|
|
|
|
+ if hasBad<>good then
|
|
|
|
+ begin
|
|
|
|
+ always:=hasBad=ALWAYS_BAD;
|
|
|
|
+ mark_bad(g_islandInfo[iWord], i, EVEN, always);
|
|
|
|
+ mark_bad(flipped^,cardinal(g_flip[i]), ODD, always);
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ flipped^.is_known[OPEN,odd]:=[0..31];
|
|
|
|
+ g_islandInfo[iWord].is_known[OPEN,even]:=[0..31];
|
|
|
|
+ end
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure record_solution(var s:Soln);
|
|
|
|
+
|
|
|
|
+var spun:soln;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ s.setcells;
|
|
|
|
+ inc(m_nSoln,2); {add solution and its rotation}
|
|
|
|
+
|
|
|
|
+ if m_minSoln.isEmpty then
|
|
|
|
+ begin
|
|
|
|
+ m_minSoln := s;
|
|
|
|
+ m_maxSoln := s;
|
|
|
|
+ exit;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ if s.lessThan(m_minSoln) then
|
|
|
|
+ m_minSoln := s
|
|
|
|
+ else if m_maxSoln.lessThan(s) then
|
|
|
|
+ m_maxSoln := s;
|
|
|
|
+
|
|
|
|
+ s.spin(spun);
|
|
|
|
+ if spun.lessThan(m_minSoln) then
|
|
|
|
+ m_minSoln := spun
|
|
|
|
+ else if m_maxSoln.lessThan(spun) then
|
|
|
|
+ m_maxSoln := spun;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function gen_all_solutions(boardVec,placedPieces:bitvec;row:byte):cardinal;
|
|
|
|
+
|
|
|
|
+var ipiece:piecenr;
|
|
|
|
+ iorient:byte;
|
|
|
|
+ piece:bitvec;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ while boardVec*TOP_ROW=TOP_ROW do
|
|
|
|
+ begin
|
|
|
|
+ cardinal(boardVec):=cardinal(boardvec) shr N_COL;
|
|
|
|
+ inc(row);
|
|
|
|
+ end;
|
|
|
|
+ gen_all_solutions:=0;
|
|
|
|
+ with cache[((cardinal(boardvec)*
|
|
|
|
+ (cardinal(placedpieces) {shl 3} + 1)
|
|
|
|
+ xor row shl 5)) mod 131071] do
|
|
|
|
+ if (krow<>row) or (bitvec(cardinal(kpiecevec))<>placedpieces) or (kboardvec<>boardvec) then
|
|
|
|
+ begin
|
|
|
|
+ with g_okpieces[row,s_firstOne[cardinal([0..N_COL-1]-boardVec)]] do
|
|
|
|
+ for ipiece:=0 to N_PIECE_TYPE-1 do
|
|
|
|
+ if not(ipiece in placedpieces) then
|
|
|
|
+ for iorient:=1 to npieces[ipiece] do {start with 1, npieces[x] can be zero}
|
|
|
|
+ begin
|
|
|
|
+ piece:=pieceVec[iPiece,iOrient-1];
|
|
|
|
+ {check if piece conflicts with other pieces or if we get a bad island.}
|
|
|
|
+ if (piece*boardVec=[]) and (has_bad_islands(boardVec+piece,row)=good) then
|
|
|
|
+ begin
|
|
|
|
+ m_curSoln.pushPiece(piece,iPiece,row);
|
|
|
|
+ {recurse or record solution}
|
|
|
|
+ if placedPieces+[ipiece]<>ALL_PIECE_MASK then
|
|
|
|
+ inc(gen_all_solutions,gen_all_solutions(boardVec+piece,placedPieces+[ipiece],row))
|
|
|
|
+ else
|
|
|
|
+ begin
|
|
|
|
+ record_solution(m_curSoln);
|
|
|
|
+ inc(gen_all_solutions);
|
|
|
|
+ end;
|
|
|
|
+ m_curSoln.popPiece();
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ if gen_all_solutions=0 then
|
|
|
|
+ begin
|
|
|
|
+ krow:=row;
|
|
|
|
+ kpiecevec:=word(cardinal(placedpieces));
|
|
|
|
+ kboardvec:=boardvec;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+begin
|
|
|
|
+ if paramcount > 2 then
|
|
|
|
+ halt(1); {spec says this is an error}
|
|
|
|
+
|
|
|
|
+ textrec(output).flushfunc:=nil;
|
|
|
|
+
|
|
|
|
+ gen_all_orientations;
|
|
|
|
+ calc_always_bad;
|
|
|
|
+ init_board;
|
|
|
|
+ filldword(cache,sizeof(cache) shr 2,$ffffffff);
|
|
|
|
+ gen_all_solutions([], [], 0);
|
|
|
|
+
|
|
|
|
+ writeln(m_nSoln,' solutions found');
|
|
|
|
+ writeln;
|
|
|
|
+ m_minSoln.write(output);
|
|
|
|
+ writeln;
|
|
|
|
+ m_maxSoln.write(output);
|
|
|
|
+ writeln;
|
|
|
|
+end.
|