program StarryNight; { by Artur Miguel Dias / 1998 } { maxSize=100*100, maxClusters=500, maxShapes=26, maxClusterSize=100 } const maxMap = 100; maxMap1 = maxMap - 1; maxShapes = 26; maxShapes1 = maxShapes - 1; type Pt = record x, y: integer end; StartPts = array[0..7] of Pt; Shape = record name: char; {identification of the shape} start: StartPts; {start points for cluster comparison} nStars: integer; {number of stars in this shape} num: integer; {number of clusters with this shape (for debugging)} end; var map: array[0..maxMap1, 0..maxMap1] of char; {the Map} size: Pt; {the Map's size} minP, maxP: Pt; {info on the current cluster} nStars: integer; {info on the current cluster} shapes: array[0..maxShapes1] of Shape; {arrows: the neighbourhood of each point in relative coordinates arrows} arrows: array[0..7] of Pt; procedure InitGlobals; begin arrows[0].x := 1; arrows[0].y := 0; arrows[1].x := 1; arrows[1].y := 1; arrows[2].x := 0; arrows[2].y := 1; arrows[3].x := -1; arrows[3].y := 1; arrows[4].x := -1; arrows[4].y := 0; arrows[5].x := -1; arrows[5].y := -1; arrows[6].x := 0; arrows[6].y := -1; arrows[7].x := 1; arrows[7].y := -1 end; procedure st (p: Pt; n: char); begin map[p.y, p.x] := n end; function gt (p: Pt): char; begin if p.x < 0 then gt := '1' else if p.x >= size.x then gt := '0' else if p.y < 0 then gt := '0' else if p.y >= size.y then gt := '0' else gt := map[p.y][p.x] end; {Walks one unit in the direction "arrow" using the co-ordinate} {system represented by "rotSym". The values of "rotSym" range from 0 to 7} {and have the following meanings: } { 0=0 1=90 1=180 3=270 4=0s 5=90s 6=180s 7=270s} {For example 7 represents a 270 rotation followed by a symetry} procedure Walk (p: Pt; arrow: integer; rotSym: integer; var res: Pt); begin if arrow = -1 then res := p else begin arrow := (arrow + 2 * (rotSym mod 4)) mod 8; if rotSym div 4 = 0 then begin res.x := p.x + arrows[arrow].x; res.y := p.y + arrows[arrow].y end else begin res.x := p.x + arrows[arrow].y; res.y := p.y + arrows[arrow].x end; end end; procedure LoadMap (fname: string); var f: text; w, h, i, j: integer; p: Pt; c: char; begin assign(f, fname); reset(f); readln(f, size.x); readln(f, size.y); for i := 0 to size.y - 1 do begin for j := 0 to size.x - 1 do begin p.x := j; p.y := i; read(f, c); st(p, c); end; readln(f) end; close(f) end; procedure WriteMap (fname: string); var f: text; i, j: integer; p: Pt; begin assign(f, fname); rewrite(f); for i := 0 to size.y - 1 do begin for j := 0 to size.x - 1 do begin p.x := j; p.y := i; write(f, gt(p)) end; writeln(f) end; close(f) end; {Determinates the enclosing rectangle of the current cluster} procedure UpdateMinMax (p: Pt); begin nStars := succ(nStars); if p.x < minP.x then minP.x := p.x; if p.x > maxP.x then maxP.x := p.x; if p.y < minP.y then minP.y := p.y; if p.y > maxP.y then maxP.y := p.y end; {Fills a cluster with the char "name" (for the first time)} procedure FirstFill (p: Pt; arrow: integer; name: char); var a: integer; np: Pt; begin Walk(p, arrow, 0, np); if (gt(np) <> '0') and (gt(np) <> name) then begin st(np, name); UpdateMinMax(np); for a := 0 to 7 do FirstFill(np, a, name); end end; {Fills a cluster with the char "name"} procedure Fill (p: Pt; arrow: integer; name: char); var a: integer; np: Pt; begin Walk(p, arrow, 0, np); if (gt(np) <> '0') and (gt(np) <> name) then begin st(np, name); for a := 0 to 7 do Fill(np, a, name); end end; {Determines the start point where the comparison of the current cluster must begin for} { the orientation "rotSym" } procedure StartPt (rotSym: integer; name: char; var res: Pt); var p: Pt; begin Walk(minP, 1, rotSym, p); res := minP; if p.x < minP.x then res.x := maxP.x; if p.y < minP.y then res.y := maxP.y; while gt(res) <> name do Walk(res, 0, rotSym, res); end; {Compares two clusters according to the orientation "rotSym"} function CompareX (p, q: Pt; arrow: integer; rotSym: integer): boolean; label 99; var a: integer; np, nq: Pt; begin Walk(p, arrow, 0, np); Walk(q, arrow, rotSym, nq); if gt(np) = '0' then begin CompareX := gt(nq) = '0'; goto 99 end; if gt(np) = '1' then begin CompareX := true; goto 99 end; st(np, '1'); if gt(nq) = '0' then begin CompareX := false; goto 99 end; for a := 0 to 7 do if not CompareX(np, nq, a, rotSym) then begin CompareX := false; goto 99 end; CompareX := true; 99: end; {Compares the cluster starting at "p" with the cluster with starting points "start"} function Compare (p: Pt; var start: StartPts; old: char): boolean; label 99; var rotSym: integer; begin for rotSym := 0 to 7 do begin if CompareX(p, start[rotSym], -1, rotSym) then begin Compare := true; goto 99 end else Fill(p, -1, old) end; Compare := false; 99: end; {Scans the map, finds the clusters, paints them} procedure ProcessMap; label 77, 99; var p: Pt; k, rotSym, nShapes: integer; i, j: integer; name: char; begin nShapes := 0; name := 'a'; for j := 0 to size.y - 1 do for i := 0 to size.x - 1 do begin p.x := i; p.y := j; if gt(p) = '1' then begin minP := p; maxP := p; nStars := 0; FirstFill(p, -1, name); for k := 0 to nShapes - 1 do if shapes[k].nStars = nStars then if Compare(p, shapes[k].start, name) then begin {Same shape} shapes[k].num := succ(shapes[k].num); Fill(p, -1, shapes[k].name); goto 77; end; {New shape} if nShapes = maxShapes then begin write('Too many shapes'); goto 99 end; shapes[nShapes].name := name; shapes[nShapes].nStars := nStars; shapes[nShapes].num := 1; for rotSym := 0 to 7 do StartPt(rotSym, name, shapes[nShapes].start[rotSym]); nShapes := succ(nShapes); name := succ(name); end; 77: end; 99: { for k := 0 to nShapes - 1 do} { writeln(shapes[k].name, ' = ', shapes[k].num)} end; begin InitGlobals; LoadMap('STARRY.IN'); ProcessMap; WriteMap('STARRY.OUT'); end.