2004. október 30., szombat

Making a reliable drawing procedure (No painting, but random numbers!)


Problem/Question/Abstract:

This is very simple, you say: Use a set to store drawn fields!
Yes, you can do if you don't have to many fields to draw. If you pull out more than 255 fields - then the set will say: No more room here! What will you do now?
(And for you who know-how to use arrays to such tasks, don’t read further. I doubt I can tell you any new)

Answer:

I’m not going to make a whole program for you – that’s quite easy, so you manage that yourself.

(Note that the routine is translated from Norwegian – some “funny” names can occur!)

The datatype required:
|
TDrawList = array of ShortString;

We make a dynamic array to make the procedure just that – dynamic. This makes the procedure work just as well with ten records as with ten thousand records. (Not tried – only a theory!) I’m using ShortString, but of course, you can use other datatypes, just remember to change down at the TempDrawList too.

The function-head – the variables: (The name “Draw” was occupied.)

function Drawing(ARecordCount: Integer; ARecords: TDrawList; ADrawCount: Integer):
  TDrawList;

var
  I, Nr: Integer;
  TempDrawList: array of record
    Value: ShortString;
    CanBeDrawn: Boolean;
  end;

ARecordCount: This is the number of records in…
ARecords: This is, as you may understand, all those who CAN be pulled out.
ADrawCount: The number of lucky guys.
I (the variable) are only used in For-statements, while Nr are given a value by the Random function.
TempDrawList: The “set”. This dynamic array knows who’s pulled out and who’s not, stored in the CanBeDrawn field.

The preparations:

SetLength(TempDrawList, ARecordCount);
SetLength(ARecords, ARecordCount);
SetLength(Result, ADrawCount);
for I := 0 to ARecordCount - 1 do // (1 to x = 0 to x-1, and we need to start with 0)
begin
  TempDrawList[I].Value := ARecords[I];
    // Loading the records from the argument to the variable.
  TempDrawList[I].CanBeDrawn := True; // Making sure that all records can be drawn.
end;
Randomize;

First, I allocate place in the memory for the arrays. I do not intend to explain the SetLength procedure. If you need help, consult the Delphi help file.

The next five lines are preparing the TempDrawList for the rest of the procedure, by loading in the records from the argument and setting all “CanBeDrawn”-s to “True”. (The name CanBeDrawn should really be self-explaining!)

At last, I call Randomize. Now the fun begins!

The rest:

for I := 0 to ADrawCount - 1 do // You know what I just wrote: 1 to x = 0 to x-1!
begin
  repeat
    Nr := Random(ARecordCount);
  until TempDrawList[Nr].CanBeDrawn = True;
  Result[I] := TempDrawList[Nr].Value;
  TempDrawList[Nr].CanBeDrawn := False;
end;

To explain step for step:
ADrawCount times do this:
Draw a random number from 0 to AReocordCount, and draw until the CanBeDrawn field in the TempDrawList at [Nr] is True.
Then, put the Value in the TempDrawList[Nr] in Result[I]. Result is a TDrawList, prepared above. “I” is the For counter. (And Nr is a random number, not pulled out before)
The last thing to do is to make sure that it cannot be drawn again.

  TempDrawList[Nr].CanBeDrawn := False.
End;

That’s it. Easy, isn’t it?
(Yes, you’ll say. Easy enough, but… how do I USE this function?)

Using this function:

There are two ways to use it: To make a random list, or to make a random selection from a list:

program UseFunction;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  TDrawList = array of ShortString;

var
  Selection, TheList: TDrawList;
  I: Integer;

function Drawing(ARecordCount: Integer; ARecords: TDrawList; ADrawCount: Integer):
  TDrawList; // The complete function, though without comments.

var
  I, Nr: Integer;
  TempDrawList: array of record
    Value: ShortString;
    CanBeDrawn: Boolean;
  end;

begin
  SetLength(TempDrawList, ARecordCount);
  SetLength(ARecords, ARecordCount);
  SetLength(Result, ADrawCount);
  for I := 0 to ARecordCount - 1 do
  begin
    TempDrawList[I].Value := ARecords[I];
    TempDrawList[I].CanBeDrawn := True;
  end;
  Randomize;
  for I := 0 to ADrawCount - 1 do
  begin
    repeat
      Nr := Random(ARecordCount);
    until TempDrawList[Nr].CanBeDrawn = True;
    Result[I] := TempDrawList[Nr].Value;
    TempDrawList[Nr].CanBeDrawn := False;
  end;
end;

begin
  SetLength(Selection, 3);
  SetLength(TheList, 5);
  for I := 0 to High(TheList) do
    TheList[I] := IntToStr(I);
  Selection := Drawing(Length(TheList), TheList, (Length(Selection));
    for I := 0 to High(Selection) do
      WriteLn(IntToStr(Selection[I]));
    ReadLn;
end.

If you change it to this:

SetLength(Selection, 5);

then the result will be a resorted (?) list.

Nincsenek megjegyzések:

Megjegyzés küldése