unit ZahlenPaareRaten.Forms.MainForm;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs,
Vcl.StdCtrls,
Vcl.ExtCtrls,
stateless;
// => https://github.com/SirRufo/stateless
type
TForm1 =
class( TForm )
CardPanel: TGridPanel;
TriggerTimer: TTimer;
Label1: TLabel;
procedure TriggerTimerTimer( Sender: TObject );
procedure FormShow( Sender: TObject );
procedure Label1Click( Sender: TObject );
private type
{$SCOPEDENUMS ON}
TState = ( Start, Memoize, Running, FirstDrawn, SecondDrawn, NoMatch, Match, Finished );
TTrigger = ( StartGame, Run, DrawCard, DrawNext );
TCardState = ( Bottom, Up, Hidden );
{$SCOPEDENUMS OFF}
TStateMachine = TStateMachine<TState, TTrigger>;
private
FCardStates :
array [ 0 .. 15 ]
of TCardState;
FCards :
array [ 0 .. 15 ]
of Integer;
FState : TStateMachine;
FDrawCardTrigger : TStateMachine.TTriggerWithParameters<Integer>;
FTriggerTimerTrigger: TTrigger;
procedure ConfigureStateMachine( State: TStateMachine );
procedure ResetGame;
procedure PresentCards;
procedure BuildCardControls;
procedure StartTriggerTimer( Trigger: TTrigger; Interval: Cardinal );
public
procedure AfterConstruction;
override;
procedure BeforeDestruction;
override;
published
procedure CardClick( Sender: TObject );
end;
var
Form1: TForm1;
implementation
uses
Enumerables;
// nicht enthalten
{$R *.dfm}
{ TForm1 }
procedure TForm1.AfterConstruction;
begin
inherited;
FState := TStateMachine.Create( TState.Start );
FDrawCardTrigger := FState.SetTriggerParameters<Integer>( TTrigger.DrawCard );
ConfigureStateMachine( FState );
BuildCardControls;
ResetGame;
end;
procedure TForm1.BeforeDestruction;
begin
FState.Free;
inherited;
end;
procedure TForm1.BuildCardControls;
var
Row : Integer;
Col : Integer;
Control: TButton;
begin
for Row := 0
to CardPanel.RowCollection.Count - 1
do
for Col := 0
to CardPanel.ColumnCollection.Count - 1
do
begin
Control := TButton.Create( Self );
Control.Parent := CardPanel;
Control.Margins.SetBounds( 3, 3, 3, 3 );
Control.AlignWithMargins := True;
Control.Align := alClient;
Control.OnClick := CardClick;
end;
end;
procedure TForm1.CardClick( Sender: TObject );
var
Control : TControl
absolute Sender;
CardIndex: Integer;
begin
if not( Sender
is TControl )
then
Exit;
CardIndex := CardPanel.ControlCollection.IndexOf( Control );
if ( CardIndex >= 0 )
and FState.CanFire( TTrigger.DrawCard )
then
FState.Fire<Integer>( FDrawCardTrigger, CardIndex );
end;
procedure TForm1.ConfigureStateMachine( State: TStateMachine );
var
firstCard, secondCard, matchCount: Integer;
begin
State.Configure( TState.Start )
{} .OnEntry(
procedure
begin
matchCount := 0;
ResetGame;
PresentCards;
end )
{} .Permit( TTrigger.StartGame, TState.Memoize )
{end};
State.Configure( TState.Memoize )
{} .OnEntry(
procedure
var
I: Integer;
begin
Label1.Caption := '
Merke dir jetzt die Karten';
for I := 0
to 15
do
FCardStates[ I ] := TCardState.Up;
PresentCards;
StartTriggerTimer( TTrigger.Run, 6000 );
end )
{} .OnExit(
procedure
var
I: Integer;
begin
for I := 0
to 15
do
FCardStates[ I ] := TCardState.Bottom;
PresentCards;
end )
{} .Permit( TTrigger.Run, TState.Running )
{end};
State.Configure( TState.Running )
{} .OnEntry(
procedure
begin
Label1.Caption := '
Wähle die erste Karte';
PresentCards;
end )
{} .PermitDynamic<Integer>( FDrawCardTrigger,
function(
const c: Integer ): TState
begin
if FCardStates[ c ] = TCardState.Bottom
then
Result := TState.FirstDrawn
else
Result := TState.Running;
end )
{end};
State.Configure( TState.FirstDrawn )
{} .OnEntry(
procedure
begin
Label1.Caption := '
Wähle die zweite Karte';
end )
{} .OnEntryFrom<Integer>( FDrawCardTrigger,
procedure(
const c: Integer )
begin
firstCard := c;
FCardStates[ c ] := TCardState.Up;
PresentCards;
end )
{} .PermitDynamic<Integer>( FDrawCardTrigger,
function(
const c: Integer ): TState
begin
if FCardStates[ c ] = TCardState.Bottom
then
begin
if FCards[ firstCard ] = FCards[ c ]
then
Result := TState.Match
else
Result := TState.NoMatch;
end
else
Result := TState.FirstDrawn;
end )
{end};
State.Configure( TState.SecondDrawn )
{} .OnEntryFrom<Integer>( FDrawCardTrigger,
procedure(
const c: Integer )
begin
secondCard := c;
FCardStates[ c ] := TCardState.Up;
PresentCards;
end )
{} .PermitDynamic( TTrigger.DrawNext,
function( ): TState
begin
if matchCount = 8
then
Result := TState.Finished
else
Result := TState.Running;
end )
{end};
State.Configure( TState.NoMatch )
{} .SubstateOf( TState.SecondDrawn )
{} .OnEntry(
procedure
begin
Label1.Caption := '
Schade, die Karten passen nicht';
StartTriggerTimer( TTrigger.DrawNext, 2000 );
end )
{} .OnExit(
procedure
begin
FCardStates[ firstCard ] := TCardState.Bottom;
FCardStates[ secondCard ] := TCardState.Bottom;
end )
{end};
State.Configure( TState.Match )
{} .SubstateOf( TState.SecondDrawn )
{} .OnEntry(
procedure
begin
Label1.Caption := '
Karten passen zusammen';
Inc( matchCount );
StartTriggerTimer( TTrigger.DrawNext, 500 );
end )
{} .OnExit(
procedure
begin
FCardStates[ firstCard ] := TCardState.Hidden;
FCardStates[ secondCard ] := TCardState.Hidden;
end )
{end};
State.Configure( TState.Finished )
{} .OnEntry(
procedure
begin
Label1.Caption := '
Spiel beendet. Klicke hier um nochmal zu spielen';
PresentCards;
end )
{} .Permit( TTrigger.StartGame, TState.Start )
{end};
end;
procedure TForm1.FormShow( Sender: TObject );
begin
PresentCards;
end;
procedure TForm1.Label1Click( Sender: TObject );
begin
if FState.CanFire( TTrigger.StartGame )
then
FState.Fire( TTrigger.StartGame );
end;
procedure TForm1.PresentCards;
var
Item : TCollectionItem;
Button: TButton;
begin
for Item
in CardPanel.ControlCollection
do
begin
Button := TButton( TControlItem( Item ).Control );
case FCardStates[ Item.
Index ]
of
TCardState.Bottom:
begin
Button.Visible := True;
Button.Caption := '
?';
end;
TCardState.Up:
begin
Button.Visible := True;
Button.Caption := IntToStr( FCards[ Item.
Index ] );
end;
TCardState.Hidden:
begin
Button.Visible := False;
end;
end;
end;
end;
procedure TForm1.ResetGame;
var
I : Integer;
lcards: TArray<Integer>;
begin
lcards := Enumerable
{} .Range( 1, 99 )
// Zahlen von 1 bis 99
{} .Shuffle( )
// mischen
{} .Take( 8 )
// 8 nehmen
{} .ToArray( )
// in ein Array
{end};
lcards := Enumerable
{} .From<Integer>( lcards )
// die 8 Zahlen von eben
{} .Concat( lcards )
// mit den 8 Zahlen von eben verbinden (jetzt haben wir die Paare)
{} .Shuffle( )
// mischen
{} .ToArray( )
// in ein Array
{end};
for I := 0
to 15
do
begin
FCardStates[ I ] := TCardState.Bottom;
FCards[ I ] := lcards[ I ];
end;
Label1.Caption := '
Zum Starten hier drücken';
end;
procedure TForm1.StartTriggerTimer( Trigger: TTrigger; Interval: Cardinal );
begin
TriggerTimer.Enabled := False;
TriggerTimer.Interval := Interval;
FTriggerTimerTrigger := Trigger;
TriggerTimer.Enabled := True;
end;
procedure TForm1.TriggerTimerTimer( Sender: TObject );
begin
TriggerTimer.Enabled := False;
if FState.CanFire( FTriggerTimerTrigger )
then
begin
FState.Fire( FTriggerTimerTrigger );
end;
end;
end.