Newsgroup: it.comp.lang.delphi
Titolo: Tombola...Elettronica!!!

Salve,
questo è il titolo di un mio programmino che deve estrarre i numeri da
1  a 90 in modo casuale per il gioco della tombola.
L'estrazione ovviamente è fatta con  random premendo un pulsante e fin
qui tutto bene, senonchè ovviamente ad ogni click del pulsante deve
corrispondere un nuovo numero estratto.
E qui cominciano i problemi: la funzione random teoricamente può
estrarre più volte lo stesso numero, e allora io devo controllare i
numeri precedentemente estratti così se un numero è già stato estratto
la funzione deve essere automaticamente eseguita fino a quando non
viene estratto un nuovo numero. 
Dato che ogni nuovo numero estratto si scrive in una label, io
controllo le varie caption, se la caption X (es. 10)  è presente
(alias se il numero è già stato estratto) rieseguo automaticamente la
funzione random fino a quando una label vuota non venga riempita con
un numero mai estratto. 
Tutto bene fino a che non sono stati estratti 50-70 numeri, ma come si
può intuire, quando i num. già estratti sono tanti la funzione random
deve essere eseguita decine e decine di volte per poter finalmente
estrarre un nuovo numero.....il fatto è però che ad un certo punto :
CRACK...il programma si blocca e devo resettare........
Informo che uso dopo ogni funzione random la riga di codice:
Application.Processmessages per passare il controllo a Windows, ma il
programma si blocca lo stesso. Ho pensato di controllare delle
variabili invece che le caption delle 90 label ...ma sinceramente non
saprei come implementare un controllo del genere.
Qualche idea per far funzionare il programma? 
Magari un tombolata elettronica ce la faremo l'anno prossimo visto che
quest'anno è già passato.
Grazie e Buon Anno a tutti.
Robin

1  a 90 in modo casuale per il gioco della tombola.
Ciao Robin.
Secondo me faresti prima a dichiarare un array di 90 elementi (anche
boolean), a resettarli e ad impostarli a 1 o True quando vengono
estratti...
numero: array [1..90] of boolean;
for c:=1 to 90 do
	numero[c]:=False;
in pseudocodifica:
mentre contatore numeri estratti < 90 esegui
estrai numero casuale
se numero è false allora 
	impostalo a true
	incrementa contatore numeri estratti
altrimenti 
	estrai numero casuale
Spero di essere stato chiaro... In caso contrario aspetta che io
smaltisca i postumi del cenone natalizio.. ;)
Auguri (2001)
Ciao!
Peppe
_____
ICQ #9759626
http://members.xoom.it/joe
Togli levaccà per contattarmi via email.

Rispondo al volo, spero di dire qualcosa di serio.
Per rapidità ti faccio usare alcuni componenti dell VCL
1: Carichi una TListBox con i valori (90)
2: generi un valore che ti scelga un numero della ListBox (al primo caso 1
su 90)
3: Ti registri il numero scelto
4: Togli l'elemento dalla listbox
5: riparti dal punto numero 2 ricordandoti che la listbox ha un elemento in
meno
ciao
"Robin" <vac...@NOSPAMkrol.it> ha scritto nel messaggio
news:ml5q4ts86h4g1uol5...@4ax.com...



Ehhmmm!! Scusa ma non ho capito tantissimo.
:-)
Magari se ti spiegassi meglio, (alias più facile)
Grazie
Ciao


Magari se ti spiegassi meglio, (alias più facile)
...Scusami, effettivamente andavo di fretta: dovevo giocare a
tombola... ;) eheheheh!
Dunque, quello che volevo dirti è che hai bisogno fondamentalmente di
2 cose:
1) un generatore di numeri casuali da 1 a 90;
2) un memorizzatore dei numeri già estratti;
3) un controllore che scarti l'estrazione di numeri già sortiti;
4) lo so: non sono bravo in matematica... ;)
Considera quindi i punti 1, 2 e 3.
1) Puoi usare la funzione Random, tranquillamente e liberamente.
2) Puoi usare un array di integer o boolean di 90 elementi e impostare
a 1 (o True, è lo stesso come concetto) l'elemento dell'array
corrispondente al numero estratto.
3) Una bella "if"... cioè: se Random (1) pesca un numero che il
controllore (2) rileva come già estratto, passa avanti e tenta di
estrarre casualmente un altro numero.
Queste le strutture base... Ovviamente tutte e tre dovrai racchiuderle
in una istruzione di reiterazione (for to do, reapeat until do, while
do) altrimenti ti estrarrà un solo numero... A tale scopo si presta
molto bene il While... Avrai bisogno di un contatore che ti controlli
quanti numeri sono stati estratti fino a quel momento... impostalo a 0
per iniziare... e ricordati di impostare a 0 (o False) anche i 90
elementi dell'array di controllo.
for c:=1 to 90 do numero[c]:=0;
numeri_estratti:=0;
while numeri_estratti<90 do
begin
       //qui dentro ci metti l'algoritmo spiegato sopra... ovverp
      //(a)pesca un numero casuale;
      //(b)se non è stato estratto prima 
      //(b1)imposta a 1 (o True) il corrispondente elemento dell'array
      // numero[];
      //(b2)incrementa di 1 il contatore numeri_estratti;
end;
...Spero di essere stato più chiaro adesso... Fammi sapere! :)


      //(b)se non è stato estratto prima 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ovviamente con questo intendo
if numero[x]=0 oppure if numero[x]=False o anche if not numero... è lo
stesso.


Peppe
Certo ti faccio sapere!!!
Allora il problema non è come fare  ad estrarre i numeri, ne il
controllo degli usciti: il fatto è che quando sono estratti diciamo
60-70 numeri su 90 la funzione random per estrarre un numero non
ancora uscito deve reiterarsi 'diciamo' innumerevoli volte. 
Per cercare di non fare entrare il programma in un loop infinito che
blocca il computer ho inserito Application.Processmessages ma
nonostante tento di passare la mano a Windows il programma ad un certo
punto si blocca invariabilmente. Come fare per evitare il loop
bloccante? Esiste qualche istruzione particolare che fa la routine
possa continuare senza bloccare il pc?
Quello che mi fa inca>>are è che lo stesso programa fatto con la
stessa tecnica in VB5 funziona!!!!! Ed anche bene purtroppo.
Comunque Grazie.
Ciao
Robin

In reply of Robin <vac...@NOSPAMkrol.it> 's  message dated Thu, 04
Jan 2001 23:40:21 GMT:
[...]
Sì, effettivamente hai ragione... Allora facciamo così, simuliamo
realmente la classica estrazione di numeri dal sacco... Una volta che
il numero viene estratto, non è più dentro il sacco... Questo si attua
ricopiando l'array su se stesso evitando di ricopiare il numero
estratto e scegliendo random non più il numero ma la posizione
all'interno dell'array che contiene i numeri...
Metti conto che dobbiamo estrarre casualmente tutti e 3 numeri da un
array di 3 numeri.
Situazione iniziale:
n[1]:=1;
n[2]:=2;
n[3]:=3;
Random dà 1, e allora il numero 1 viene tolto dalla sua posizione e
sulla posizione che questo occupava viene ricopiato quello seguente e
così per gli altri:
n[1]:=2;
n[2]:=3
n[3]:=3; < questo resta così perchè è l'ultimo elemento, ma non fa
testo in quanto adesso verranno estratti casualmente gli elementi
compresi tra 1 e 2.
Per comodità ti ho scritto il codice in Delphi... Io l'ho testato e
funziona... è veloce e DOVREBBE essere affidabile... Non ci credi?
Provalo! ;)
procedure TForm1.TombolaClick(Sender: TObject);
var
   c, k, estratto, da_estrarre: integer;
   n: array [1..90] of integer;
begin
     Randomize;
     for c:=1 to 90 do n[c]:=c;
     da_estrarre:=90;
     while da_estrarre>0 do
     begin
          estratto:=Random(da_estrarre)+1;
          RichEdit1.Lines.Add(IntToStr(n[estratto]));
          Dec(da_estrarre);
          k:=0;
          for c:=1 to da_estrarre do
          begin
               if c = estratto then Inc(k);
               n[c]:=n[c+k];
          end;
     end;
end;
In bocca al lupo! :)



    Diciamo che non esiste una procedura che fa esattamente quello che
chiedi, ma la cosa si può risolvere in maniera elegante utilizzando qualche
struttura dati che sta alla base della storia dell'informatica.
    Ti posso dire quello che farei io. Io creeerei una lista concatenata
doppia circolare di 90 elementi, ogni volta che estraggo un elemento elimino
un nodo dalla lista concatenata che mano a mano diventa sempre più piccola.
Poi, utilizzerei la funzione Random con selezione casuale da 1 e 90 e
scorrerei la lista sempre in avanti, anche se ha meno di 90 elementi, poiché
,essendo circolare selezionerà sempre un numero. Una volta estratto, si
elimina il numero dalla lista. Così la funzione non dovrà più ciclare
all'infinito e la tu a condizione di stop sarà la distruzione della lista.
Così risolvi elegantemente il tuo problema...
    Se vuoi scrivimi in privato, così ti faccio vedere come si fa...
    Saluti!
    P.S. Se ho un po' di tempo provo a creare una classe che implementi una
lista circolare doppia di interi...
_________________________________
Gian Luca Dalla Torre
E - Mail: 
gianluca....@casteo.net
_________________________________

 struttura dati che sta alla base della storia dell'informatica.
Per esempio un Set of 1..90 ? :-)
--
Andrea Magni
E-Mail: 
ama...@interac.it

ancora uscito deve reiterarsi 'diciamo' innumerevoli volte.
abbiamo un array N [0..89] of byte, lo inizializziamo in questo modo:
for x := 0 to 89 do  N[x] := x;
Limite := 89;
1)
si estrae un numero a caso tra 0 e Limite, ad esempio 10.
il numero estratto risulta essere N[10].
scambiamo N[10] con N[Limite] e decrementiamo Limite.
ripetiamo dal punto 1) finche non abbiamo estratto tutti i
numeri.
Questa mi pare la soluzione piu' veloce sia come implementazione
sia come tempo di esecuzione.

for x := 0 to 89 do  N[x] := x;
pardon:  N[x] := x + 1;
(meno male che non c'e' piu' Nando altrimenti... :-)

Insisito, dato che il primo messaggio e' stato ignorato e si e' preso un
complesso di ignorante:-),
come vi semba:
Contando che:
- Prima va chiamata la funzione CaricaClick, poi per tot volta la funzione
Estraeclick e infine la funzione ScaricaClick,
- Estrai e' un campo memo
Var Numeri : TStringList;
implementation
{$R *.DFM}
procedure TForm1.CaricaClick(Sender: TObject);
Var C1 : Integer;
begin
  Numeri := TStringList.Create;
  For c1 := 1 to 90 do Numeri.Add(INtToSTr(c1));
  Randomize;
end;
procedure TForm1.EstraeClick(Sender: TObject);
Var Quale : Integer;
begin
  Quale := Random(Numeri.Count);
  Estratti.Lines.Add(Numeri[Quale]);
  Numeri.Delete(quale);
end;
procedure TForm1.SCaricaClick(Sender: TObject);
begin
  Numeri.Free;
end;
ciao

complesso di ignorante:-),
Complimenti!!! Funziona perfettamente. 
Però il suggerimento precedente non lo avevo capito perfettamente.
Adesso è chiaro e funziona bene.
Grazie.
Ciao
Robin

  Numeri.Free;
mi pare che per una cosa del genere non sia necessario
scomodare una stringlist, comunque data la situazione
si puo' anche fare.
personalmente il create e free dellostringlist li metterei
nel OnCreate e OnDestroy del form e nel "carica"
metterei solo un clear (al posto del create); questo
perche' altrimenti dai la possibilita' all'utente di
generare una violazione di accesso semplicemente
usando male i bottoni. (spero di aver capito bene la
situazione...)
p.s.
hai risolto il problema della divisione per 0 ?


Per il momento e' fermo, ma quello che piu' mi interessava era capire
perche' questo succede.