15 パズルは、 1 から 15 までのパネルを正しく並べ替えるゲームです。
uCells.pas
unit uCells; interface uses SysUtils, Extctrls, Graphics, Types; const MAX_ROW_COUNT = 4; MAX_COL_COUNT = 4; MAX_CELL_COUNT = 16; EMPTY_CELL = -1; type TCellCollection = class; TCellCanvas = class private FBox: TPaintBox; FCellWidth: Integer; FCellHeight: Integer; FCellCollection: TCellCollection; procedure DoDrawCell(X, Y: Integer; s: string); procedure DrawLine; procedure DrawCell; overload; procedure DrawCell(TargetCellNum, EmptyCellNum: Integer); overload; function GetClickedCellNumber(X, Y: Integer): Integer; public constructor Create(Box: TPaintBox); destructor Destroy; override; procedure Initialize; procedure CellClick(X, Y: Integer); end; TCellCollection = class private FCells: array[0..MAX_CELL_COUNT-1] of Integer; procedure Shuffle; function GetCell(index: Integer): Integer; public procedure Initialize; function GetEmptyCellNum: Integer; procedure Exchange(x: Integer); function IsExchange(x: Integer): Boolean; property Cells[index: Integer]: Integer read GetCell; default; end; implementation { TFreeCanvas } function GetCol(x: Integer): Integer; begin Result := x mod MAX_COL_COUNT; end; function GetRow(x: Integer): Integer; begin Result := x div MAX_ROW_COUNT; end; procedure TCellCanvas.CellClick(X, Y: Integer); begin if FCellCollection.IsExchange(GetClickedCellNumber(X, Y)) then begin DrawCell(GetClickedCellNumber(X, Y), FCellCollection.GetEmptyCellNum); FCellCollection.Exchange(GetClickedCellNumber(X, Y)); end; end; constructor TCellCanvas.Create(Box: TPaintBox); begin FBox := Box; FCellCollection := TCellCollection.Create; end; destructor TCellCanvas.Destroy; begin FCellCollection.Free; inherited; end; procedure TCellCanvas.DrawCell; var i, j, Count: Integer; s: string; begin Count := 0; for i := 0 to MAX_ROW_COUNT-1 do for j := 0 to MAX_COL_COUNT-1 do begin if FCellCollection[Count] = EMPTY_CELL then s := ' ' else s := IntToStr(FCellCollection[Count]); DoDrawCell(j*FCellWidth+5, i*FCellHeight+5, s); Inc(Count); end; end; procedure TCellCanvas.DoDrawCell(X, Y: Integer; s: string); begin FBox.Canvas.TextOut(X, Y, s); end; procedure TCellCanvas.DrawCell(TargetCellNum, EmptyCellNum: Integer); begin DoDrawCell(GetCol(EmptyCellNum)*FCellWidth+5, GetRow(EmptyCellNum)*FCellHeight+5, IntToStr(FCellCollection[TargetCellNum])); DoDrawCell(GetCol(TargetCellNum)*FCellWidth+5, GetRow(TargetCellNum)*FCellHeight+5, ' '); end; procedure TCellCanvas.DrawLine; var i: Integer; begin FBox.Canvas.Pen.Color := clGray; for i := 1 to MAX_COL_COUNT-1 do begin FBox.Canvas.MoveTo(i*FCellWidth, 0); FBox.Canvas.LineTo(i*FCellWidth, FBox.ClientHeight); end; for i := 1 to MAX_ROW_COUNT-1 do begin FBox.Canvas.MoveTo(0, i*FCellHeight); FBox.Canvas.LineTo(FBox.ClientWidth, i*FCellHeight); end; end; function TCellCanvas.GetClickedCellNumber(X, Y: Integer): Integer; var Col, Row: Integer; begin Col := X div FCellWidth; Row := Y div FCellHeight; Result := Col + MAX_COL_COUNT*Row; end; procedure TCellCanvas.Initialize; begin FBox.Canvas.Brush.Color := clWhite; FBox.Canvas.FillRect(FBox.ClientRect); FCellWidth := FBox.ClientWidth div MAX_COL_COUNT; FCellHeight := FBox.ClientHeight div MAX_ROW_COUNT; FBox.Canvas.Font.Height := FCellHeight div 2; FCellCollection.Initialize; DrawLine; DrawCell; end; { TCellCollection } procedure TCellCollection.Exchange(x: Integer); var tmp, num: Integer; begin num := GetEmptyCellNum; tmp := FCells[x]; FCells[x] := FCells[num]; FCells[num] := tmp; end; function TCellCollection.GetCell(index: Integer): Integer; begin Result := FCells[index]; end; function TCellCollection.GetEmptyCellNum: Integer; var i: Integer; begin Result := 0; for i := 0 to High(FCells) do if FCells[i] = EMPTY_CELL then begin Result := i; exit; end; end; procedure TCellCollection.Initialize; var i: Integer; begin for i := 0 to High(FCells)-1 do fCells[i] := i+1; fCells[High(FCells)] := EMPTY_CELL; Shuffle; end; function TCellCollection.IsExchange(x: Integer): Boolean; var Num, Row, Col, ClickedRow, ClickedCol: Integer; begin Result := false; Num := GetEmptyCellNum; Row := GetRow(Num); Col := GetCol(Num); ClickedRow := GetRow(x); ClickedCol := GetCol(x); if ((Abs(Row-ClickedRow) = 0) and (Abs(Col-ClickedCol) = 1)) or ((Abs(Row-ClickedRow) = 1) and (Abs(Col-ClickedCol) = 0)) then Result := true; end; procedure TCellCollection.Shuffle; function HasAnswer: Boolean; var i, j, tmp, Count: Integer; begin Count := 0; for i := 0 to High(FCells)-1 do begin tmp := FCells[i]; for j := i+1 to High(FCells)-1 do if tmp > FCells[j] then Inc(Count); end; Result := (Count mod 2) = 0; end; var i, len, a, b, tmp: Integer; begin len := High(FCells); for i := 0 to len do begin a := Random(len); b := Random(len); tmp := FCells[a]; FCells[a] := FCells[b]; FCells[b] := tmp; end; if not HasAnswer then Shuffle; end; end.
Main.pas
unit Main; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, uCells; type TForm1 = class(TForm) PaintBox1: TPaintBox; Button1: TButton; PaintBox2: TPaintBox; PaintBox3: TPaintBox; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure PaintBox3MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure PaintBox1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure PaintBox2MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); private { Private 宣言 } FCellCanvas1: TCellCanvas; FCellCanvas2: TCellCanvas; FCellCanvas3: TCellCanvas; public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin Randomize; FCellCanvas1 := TCellCanvas.Create(PaintBox1); FCellCanvas2 := TCellCanvas.Create(PaintBox2); FCellCanvas3 := TCellCanvas.Create(PaintBox3); end; procedure TForm1.FormDestroy(Sender: TObject); begin FCellCanvas1.Free; FCellCanvas2.Free; FCellCanvas3.Free; end; procedure TForm1.Button1Click(Sender: TObject); begin FCellCanvas1.Initialize; FCellCanvas2.Initialize; FCellCanvas3.Initialize; end; procedure TForm1.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin FCellCanvas1.CellClick(X, Y); end; procedure TForm1.PaintBox2MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin FCellCanvas2.CellClick(X, Y); end; procedure TForm1.PaintBox3MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin FCellCanvas3.CellClick(X, Y); end; end.