unit raetselunit; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, Spin, Controls, StdCtrls, ExtCtrls, LCLType, lowlevelunit, Messages, ComCtrls; type tAlphabetFunktion = function(i: longint): string; tButtonWithArrowKeys = class(tButton) private procedure WMGetDlgCode(var Msg: TWMGetDlgCode); message WM_GETDLGCODE; end; tRaetsel = class private besitzer: tForm; zeichenflaeche: tImage; erzeugeBtn: tButtonWithArrowKeys; progressbar1: tProgressBar; function besitzerHoehe: longint; dynamic; function besitzerBreite: longint; dynamic; procedure zeichenFlaecheNeuKreieren; procedure onKeyDown(sender: tObject; var key: word; shiftState: tShiftState); dynamic; abstract; procedure onMouseDown(sender: tObject; button: tMouseButton; shiftState: tShiftState; x,y: longint); dynamic; abstract; procedure erzeugeOnClick(sender: tObject); procedure loeschen; dynamic; abstract; procedure leeren; dynamic; abstract; function loesen(inhaltBehalten: boolean; lPos: longint): longint; dynamic; abstract; function passtZumZeichnen(spalte,zeile: integer): boolean; dynamic; abstract; function passt(spalte,zeile: integer): boolean; dynamic; abstract; function geloest: boolean; dynamic; abstract; public constructor create(aOwner: tForm); destructor destroy; override; procedure zeichnen; dynamic; abstract; end; tFelderRaetsel = class(tRaetsel) spinEdits: array of tSpinEdit; diagonalenCB,sudokuCB: tCheckBox; groeszen,inhalt,rand: array of longint; moeglich: array of int64; dim,schriftGroesze,cursorPosition: longint; zellGroesze: extended; uebersetze: tAlphabetFunktion; procedure anzSEsOnChange(sender: tObject); procedure onKeyDown(sender: tObject; var key: word; shiftState: tShiftState); override; procedure onMouseDown(sender: tObject; button: tMouseButton; shiftState: tShiftState; x,y: longint); override; procedure aktualisiereGroesze; procedure loeschen; override; procedure leeren; override; procedure aktualisiereZeichenflaechenGroesze; function besitzerHoehe: longint; override; function besitzerBreite: longint; override; procedure schreibeZentriert(x,y,i: longint); procedure relativeInhaltsAenderung(diff: longint); dynamic; abstract; function absoluteInhaltsAenderung(key: word): boolean; dynamic; abstract; public constructor create(aOwner: tForm; anzInhTypen: longint; alphabetFunktion: tAlphabetFunktion); destructor destroy; override; procedure zeichnen; override; end; {$DEFINE interface} {$DEFINE hochhaus} {$I raetselunit.inc} {$UNDEF hochhaus} {$DEFINE buchstaben} {$I raetselunit.inc} {$UNDEF buchstaben} {$UNDEF interface} const spacing = 2; implementation uses math, dialogs; {$DEFINE alphabetFunktion} function zahlenAlphabetFunktion(i: longint): string; {$DEFINE hochhaus} {$I raetselunit.inc} {$UNDEF hochhaus} function buchstabenAlphabetFunktion(i: longint): string; {$DEFINE buchstaben} {$I raetselunit.inc} {$UNDEF buchstaben} {$UNDEF alphabetFunktion} // tButtonWithArrowkeys ******************************************************** procedure tButtonWithArrowkeys.WMGetDlgCode(var msg: tWMGetDLGCODE);// message WM_GETDLGCODE; begin inherited; msg.Result := msg.Result or DLGC_WANTARROWS; end; // tRaetsel ******************************************************************** constructor tRaetsel.create(aOwner: tForm); begin inherited create; besitzer:=aOwner; zeichenflaeche:=tImage.create(besitzer); zeichenflaeche.parent:=besitzer; zeichenflaeche.onMouseDown:=@onMouseDown; erzeugeBtn:=tButtonWithArrowKeys.create(besitzer); erzeugeBtn.parent:=besitzer; erzeugeBtn.left:=spacing;; erzeugeBtn.top:=spacing;; erzeugeBtn.caption:='Erzeugen!'; erzeugeBtn.onClick:=@erzeugeOnClick; erzeugeBtn.onKeyDown:=@onKeyDown; progressbar1:=tProgressBar.create(besitzer); progressbar1.visible:=false; progressbar1.parent:=besitzer; end; destructor tRaetsel.destroy; begin zeichenflaeche.free; inherited destroy; end; function tRaetsel.besitzerHoehe: longint; begin result:=zeichenflaeche.height+zeichenflaeche.top+spacing; end; function tRaetsel.besitzerBreite: longint; begin result:=zeichenflaeche.width+zeichenflaeche.left+spacing; end; procedure tRaetsel.zeichenFlaecheNeuKreieren; var i: tImage; begin i:=tImage.create(besitzer); i.parent:=zeichenflaeche.parent; i.left:=zeichenflaeche.left; i.top:=zeichenflaeche.top; i.width:=zeichenflaeche.width; i.height:=zeichenflaeche.height; i.canvas.font.size:=zeichenflaeche.canvas.font.size; i.onMouseDown:=zeichenflaeche.onMouseDown; zeichenflaeche.free; zeichenflaeche:=i; end; procedure tRaetsel.erzeugeOnClick(sender: tObject); begin loeschen; loesen(true,-1); leeren; end; // tFelderRaetsel ************************************************************** constructor tFelderRaetsel.create(aOwner: tForm; anzInhTypen: longint; alphabetFunktion: tAlphabetFunktion); var i: longint; begin inherited create(aOwner); uebersetze:=alphabetFunktion; cursorPosition:=-1; setlength(spinEdits,anzInhTypen+1); setlength(groeszen,length(spinEdits)-1); for i:=0 to length(spinEdits)-1 do begin spinEdits[i]:=tSpinEdit.create(besitzer); if i=1 then groeszen[i-1]:=5 else if i>1 then groeszen[i-1]:=1; with spinEdits[i] do begin parent:=besitzer; top:=erzeugeBtn.top+erzeugeBtn.height+spacing; if i>0 then begin left:=spinEdits[i-1].left + spinEdits[i-1].width; value:=groeszen[i-1]; end; left:=left+spacing; onChange:=@anzSEsOnChange; onKeyDown:=@self.onKeyDown; tag:=i; end; end; spinEdits[0].showHint:=true; spinEdits[0].hint:='Schriftgröße'; spinEdits[0].value:=14; diagonalenCB:=tCheckBox.create(besitzer); diagonalenCB.parent:=besitzer; diagonalenCB.caption:='Diagonalen'; diagonalenCB.top:=erzeugeBtn.top+erzeugeBtn.height+spacing; diagonalenCB.left:=spinEdits[length(spinEdits)-1].left+spinEdits[length(spinEdits)-1].width+spacing; sudokuCB:=tCheckBox.create(besitzer); sudokuCB.parent:=besitzer; sudokuCB.caption:='Sudoku'; sudokuCB.top:=erzeugeBtn.top+erzeugeBtn.height+spacing; sudokuCB.left:=diagonalenCB.left+diagonalenCB.width+spacing; zeichenflaeche.left:=spacing; zeichenflaeche.top:=diagonalenCB.top+diagonalenCB.height+spacing; aktualisiereGroesze; end; destructor tFelderRaetsel.destroy; begin inherited destroy; end; procedure tFelderRaetsel.anzSEsOnChange(sender: tObject); begin if ((sender as tSpinEdit).tag = 0) and ((sender as tSpinEdit).value <> schriftGroesze) then begin schriftGroesze:=(sender as tSpinEdit).value; aktualisiereZeichenflaechenGroesze end else if (sender as tSpinEdit).value <> groeszen[(sender as tSpinEdit).tag-1] then aktualisiereGroesze; end; procedure tFelderRaetsel.onKeyDown(sender: tObject; var key: word; shiftState: tShiftState); begin if not absoluteInhaltsAenderung(key) then case key of VK_DOWN: cursorPosition:= (cursorPosition mod dim) + dim*min(dim-1,cursorPosition div dim + 1); VK_UP: cursorPosition:= (cursorPosition mod dim) + dim*max(0,cursorPosition div dim - 1); VK_LEFT: cursorPosition:= max(0,cursorPosition mod dim - 1) + dim*(cursorPosition div dim); VK_RIGHT: cursorPosition:= min(dim-1,cursorPosition mod dim + 1) + dim*(cursorPosition div dim); 33,107,187: if (cursorPosition>=0) and (cursorPosition=0) and (cursorPosition=dim) or (y<0) or (y>=dim) then exit; cursorPosition:=x+y*dim; zeichnen; end; procedure tFelderRaetsel.aktualisiereGroesze; var i: longint; begin dim:=0; schriftGroesze:=spinEdits[0].value; for i:=0 to length(groeszen)-1 do begin groeszen[i]:=spinEdits[i+1].value; dim:=dim+groeszen[i]; end; setlength(inhalt,dim*dim); setlength(rand,4*(dim+1)); loeschen; cursorPosition:=0; aktualisiereZeichenflaechenGroesze; end; procedure tFelderRaetsel.loeschen; var i: longint; begin for i:=0 to length(inhalt)-1 do inhalt[i]:=-1; for i:=0 to length(rand)-1 do rand[i]:=-1; end; procedure tFelderRaetsel.leeren; var p: tLongintArray; i,w: longint; begin p:=permutation(dim*dim); for i:=0 to length(p)-1 do begin if inhalt[p[i]]<0 then continue; w:=inhalt[p[i]]; inhalt[p[i]]:=-1; if loesen(false,-1)<>1 then inhalt[p[i]]:=w; end; p:=permutation((dim+1)*4); for i:=0 to length(p)-1 do begin if rand[p[i]]<0 then continue; w:=rand[p[i]]; rand[p[i]]:=-1; if loesen(false,-1)<>1 then rand[p[i]]:=w; end; end; procedure tFelderRaetsel.aktualisiereZeichenflaechenGroesze; begin zeichenflaeche.canvas.font.size:=schriftGroesze; zellGroesze:= 2*spacing + zeichenflaeche.canvas.pen.width + max( zeichenflaeche.canvas.textWidth(uebersetze(dim)), zeichenflaeche.canvas.textHeight(uebersetze(dim)) ); zeichenflaeche.height:=round((dim+2)*zellGroesze); zeichenflaeche.width:=zeichenflaeche.height; zeichenFlaecheNeuKreieren; progressbar1.width:=besitzerBreite; besitzer.height:=besitzerHoehe; besitzer.width:=besitzerBreite; zeichnen; end; function tFelderRaetsel.besitzerHoehe: longint; var i: longint; begin result:=max(inherited besitzerHoehe,diagonalenCB.top+diagonalenCB.height+spacing); for i:=0 to length(spinEdits)-1 do result:=max(result,spinEdits[i].top+spinEdits[i].height+spacing); end; function tFelderRaetsel.besitzerBreite: longint; var i: longint; begin result:=max(inherited besitzerBreite,diagonalenCB.left+diagonalenCB.width+spacing);; for i:=0 to length(spinEdits)-1 do result:=max(result,spinEdits[i].left+spinEdits[i].width+spacing); end; procedure tFelderRaetsel.schreibeZentriert(x,y,i: longint); var br,ho: longint; s: string; begin s:=uebersetze(i); with zeichenflaeche.canvas do begin brush.color:=$FFFFFF - $181818*byte(diagonalenCB.checked and ((x=y) or (x+y=dim+1))); if not passtZumZeichnen(x,y) then font.color:=$0000FF else if geloest then font.color:=$007F00 // else if startFeld[x+y*dim] then // font.color:=$7F7F7F else font.color:=$000000; // feldFarben[x+y*dim]; br:=textWidth(s); ho:=textHeight(s); textOut( round((x+1.5)*zellGroesze-br/2), round((y+1.5)*zellGroesze-ho/2), s ); end; end; procedure tFelderRaetsel.zeichnen; var i: longint; begin zeichenflaeche.canvas.brush.color:=$ffffff; zeichenflaeche.canvas.rectangle(-10,-10,zeichenflaeche.width+10,zeichenflaeche.height+10); zeichenflaeche.canvas.pen.width:=3; zeichenflaeche.canvas.pen.color:=$000000; for i:=0 to dim do with zeichenflaeche.canvas do begin moveTo(round((i+1)*zellGroesze),round(zellGroesze)); lineTo(round((i+1)*zellGroesze),round((dim+1)*zellGroesze)); moveTo(round(zellGroesze),round((i+1)*zellGroesze)); lineTo(round((dim+1)*zellGroesze),round((i+1)*zellGroesze)); end; zeichenflaeche.canvas.pen.color:=$8080ff; if (cursorPosition>=0) and (dim>0) then zeichenflaeche.canvas.rectangle( round(((cursorPosition mod dim)+1)*zellGroesze), round(((cursorPosition div dim)+1)*zellGroesze), round(((cursorPosition mod dim)+2)*zellGroesze+1), round(((cursorPosition div dim)+2)*zellGroesze+1) ); for i:=0 to dim-1 do begin schreibeZentriert(i,-1,rand[i]); schreibeZentriert(dim,i,rand[dim+i]); schreibeZentriert(i,dim,rand[2*dim+i]); schreibeZentriert(-1,i,rand[3*dim+i]); end; for i:=0 to length(inhalt)-1 do schreibeZentriert(i mod dim,i div dim,inhalt[i]); end; {$DEFINE create} constructor tHochhausRaetsel.create(aOwner: tForm); {$DEFINE hochhaus} {$I raetselunit.inc} {$UNDEF hochhaus} constructor tBuchstabenRaetsel.create(aOwner: tForm); {$DEFINE buchstaben} {$I raetselunit.inc} {$UNDEF buchstaben} {$UNDEF create} {$DEFINE destroy} destructor tHochhausRaetsel.destroy; {$DEFINE hochhaus} {$I raetselunit.inc} {$UNDEF hochhaus} destructor tBuchstabenRaetsel.destroy; {$DEFINE buchstaben} {$I raetselunit.inc} {$UNDEF buchstaben} {$UNDEF destroy} {$DEFINE passt} function tHochhausRaetsel.passt(spalte,zeile: integer): boolean; {$DEFINE hochhaus} {$I raetselunit.inc} {$UNDEF hochhaus} function tBuchstabenRaetsel.passt(spalte,zeile: integer): boolean; {$DEFINE buchstaben} {$I raetselunit.inc} {$UNDEF buchstaben} {$UNDEF passt} {$DEFINE geloest} function tHochhausRaetsel.geloest: boolean; {$DEFINE hochhaus} {$I raetselunit.inc} {$UNDEF hochhaus} function tBuchstabenRaetsel.geloest: boolean; {$DEFINE buchstaben} {$I raetselunit.inc} {$UNDEF buchstaben} {$UNDEF geloest} {$DEFINE gesamtRaenderErzeugen} procedure tHochhausRaetsel.gesamtRaenderErzeugen; {$DEFINE hochhaus} {$I raetselunit.inc} {$UNDEF hochhaus} procedure tBuchstabenRaetsel.gesamtRaenderErzeugen; {$DEFINE buchstaben} {$I raetselunit.inc} {$UNDEF buchstaben} {$UNDEF gesamtRaenderErzeugen} {$DEFINE passtZumZeichnen} function tHochhausRaetsel.passtZumZeichnen(spalte,zeile: integer): boolean; {$DEFINE hochhaus} {$I raetselunit.inc} {$UNDEF hochhaus} function tBuchstabenRaetsel.passtZumZeichnen(spalte,zeile: integer): boolean; {$DEFINE buchstaben} {$I raetselunit.inc} {$UNDEF buchstaben} {$UNDEF passtZumZeichnen} end.