Mega Code Archive

 
Categories / Delphi / Examples
 

Random Number Engine with UNIQUE property

Title: Random Number Engine with UNIQUE property Question: This class can be used to generate random numbers from MinNumber to MaxNumber returning a number only once if Unique is set. A check is also available to see if the numbers has been used. A dynamic TBit array is used to ensure numbers are returned only once and to see if ALL the numbers have been used. Useful particularly for card games eg. . random numbers from 1 to 52, and once drawn do not draw same card again until pack is empty. properties ----------------- MinNumber Random Number from. MaxNumber Random Number to. NumbersTotal Total available numbers. NumbersUsed Unique numbers used of total. NumbersFree Unique numbers free of total. Unique If true then a number will not be repeated. If false then numbers may be repeated. methods --------------- IsUsed(Index) Returns true if UNIQUE used and number has been used. Reset Resets the NumbersUsed and NumbersFree (Used by UNIQUE) GetRandom Returns a random number from MinNumber to MaxNumber (will not repeat a number if UNIQUE is set) // ========================= // Simple Example // ========================= procedure TForm1.Button2Click(Sender: TObject); var R : TRandomEngine; begin R := TRandomEngine.Create; R.MinNumber := 1; R.MaxNumber := 52; R.Unique := true; repeat Memo1.Lines.Add(inttostr(R.GetRandom) + ' ' + inttostr(R.NumbersTotal) + ' ' + inttostr(R.NumbersUsed) + ' ' + inttostr(R.NumbersFree)); until R.NumbersFree = 0; R.Free; end; Answer: unit REngine; interface uses Windows,Classes; type TRandomEngine = class(TObject) private FSelected : TBits; FArrSize,FNumbersUsed : longint; FMinNumber,FMaxNumber : longint; FUnique : boolean; procedure SizeSelArray; procedure SetFMinNumber(NewValue : longint); procedure SetFMaxNumber(NewValue : longint); function GetFNumbersFree : longint; public constructor Create; destructor Destroy; override; procedure Reset; function GetRandom : longint; function IsUsed(Index : longint) : boolean; property MinNumber : longint read FMinNumber write SetFMinNumber; property MaxNumber : longint read FMinNumber write SetFMaxNumber; property Unique : boolean read FUnique write FUnique; property NumbersUsed : longint read FNumbersUsed; property NumbersTotal : longint read FArrSize; property NumbersFree : longint read GetFNumbersFree; end; // ------------------------------------------------------------- implementation // =================================== // Create and Free the Object // =================================== constructor TRandomEngine.Create; begin FSelected := TBits.Create; FNumbersUsed := 0; FMinNumber := 0; FMaxNumber := 0; FArrSize := 0; FUnique := false; Randomize; end; destructor TRandomEngine.Destroy; begin inherited Destroy; FSelected.Free; end; // =========================== // Property Get/Set methods // =========================== procedure TRandomEngine.SetFMinNumber(NewValue : longint); begin if (NewValue FMinNumber) then begin FMinNumber := NewValue; if FMinNumber FMaxNumber then FMaxNumber := FMinNumber; SizeSelArray; end; end; procedure TRandomEngine.SetFMaxNumber(NewValue : longint); begin if (NewValue FMaxNumber) then begin FMaxNumber := NewValue; if FMaxNumber SizeSelArray; end; end; function TRandomEngine.GetFNumbersFree : longint; begin Result := FArrSize - FNumbersUsed; end; // ======================================= // Resize the boolean array (FSelected) // ======================================= procedure TRandomEngine.SizeSelArray; var i : longint; begin FArrSize := FMaxNumber - FMinNumber + 1; if FArrSize 0 then begin FSelected.Size := FArrSize; for i := 0 to FArrSize - 1 do FSelected[i] := false; end; FNumbersUsed := 0; end; // ======================================= // Reset avail,used and free numbers. // Reset FSelected array to false for // IsUsed() // ======================================= procedure TRandomEngine.Reset; begin SizeSelArray; end; // =================================================== // Return true/false if numbers has been used if // Unique is set // =================================================== function TRandomEngine.IsUsed(Index : longint) : boolean; var Retvar : boolean; begin if (Index FMaxNumber) then Retvar := false else RetVar := FSelected[Index - FMinNumber]; Result := RetVar; end; // =================================================== // Return a random number based on Min - Max // If Unique then generate based on FSelected // array (ie. make sure number has not been used // =================================================== function TRandomEngine.GetRandom : longint; var V : longint; NumSelected : boolean; begin if FUnique and (FNumbersUsed = FArrSize) then V := 0 else begin repeat V := Random(FMaxNumber - FMinNumber + 1) + FMinNumber; if not FUnique then NumSelected := true else begin if FSelected[V - FMinNumber] then NumSelected := false else begin NumSelected := true; FSelected[V - FMinNumber] := true; inc(FNumbersUsed); end; end; until NumSelected; end; Result := V; end; end.