Tabela Ping-pong dla 24 zawodników

0

Witam, musze napisać program, w którym nauczyciel wypełnia tabelkę nazwiskami i zaznacza kto wygrał mecz.

Przedstawiam tabele w załączniku.

Problem polega na tym, że nie bardzo ją rozumiem. Liczby w pierwszej kolumnie to osoby, jeżeli osoba nr 18 wygra mecz z nr 15, to osoba nr 18 przyjmuje indeks 1 i wtedy gra z graczem nr 2?
Gdy osoba nr 10 przegra mecz z osobą nr 23, to osoba nr 10 przyjmuje nr (indeks) -10 i gra wtedy z os. nr -3?

Może mi to ktoś wytłumaczyć?

Program będę pisał w Delphi 7 (Pascal).

Pozdrawiam

0

Nie wiem, czy nikt nie grał w tenisa w szkole, czy ta tabelka jest aż tak dziwna;

Grałem przez wiele lat na różnych zawodach w tenisa stołowego (szkolnych, powiatowych, rejonowych itp.) i jeszcze tak dziwnej tabeli nie widziałem; Wygląda, jakby grało się do trzech przegranych, a nie do dwóch jak w systemie pucharowym; Poza tym nie było jakichś dziwacznych minusów, tylko tabelka była dwustronna - jeśli się wygrało mecz idzie się dalej w prawo według kolumny z danymi zawodników, jeśli przegrało - w odpowiednie miejsce z lewej strony od tej kolumny;

Przykłady poprawnych tabel są jak zwykle do znalezienia w Google - pierwsze z brzegu znalezione na stronie Podlaskiego Okręgowego Związku Tenisa Stołowego (którą na większości jak nie wszystkich zawodów miałem okazję przeglądać) do zobaczenia tutaj: http://pozts.org/druki-do-pobrania/Druki-do-pobrania/Tabele-turniejowe/

Jak widać nie ma w niej żadnych dziwnych kombinacji - wszystko jest klarowne; Niestety (a może i na szczęście) tabele te robione są pod ilość zawodników równą kolejnym potęgom dwójki: 16, 32, 64, 128 i takie też nie są przekombinowane; W Twojej natomiast jedna trzecia zawodników przechodzi bez pierwszego meczu, co jest idiotyzmem w przypadku pełnej listy zawodników;


Jeśli chodzi o Twoją tabelę - pierwsza jej część (górna połowa strony) to tabela dla wygranych kolejnych meczy; Numery na lewym marginesie oznaczają ID zawodnika, zaś numery od 1 do 22 wewnątrz drzewa to nowe ID zawodników, którzy mecz przegrają (do tego numeru dodaje się minus i wpisuje delikwenta w drugiej części tabeli w odpowiednie miejsce o takim numerze);

Trzecia część tabeli najprawdopodobniej odgrywa taką samą rolę jak druga, tyle że aby się w niej znaleźć trzeba przegrać drugi mecz (czyli przegrać dwa razy, niekoniecznie po kolei); Takiego zawodnika przenosi się z drugiej części tabeli do trzeciej na takiej samej zasadzie, jak z pierwszej części do drugiej - ID przegranego zawodnika minusuje się i wstawia się go w odpowiednie miejsce w trzeciej części tabeli;

Najprawdopodobniej jest to system "do trzech przegranych", a nie do dwóch, jak w standardowym systemie pucharowym; Stąd też nieco większy zamęt, ale zasada podobna, choć nie wiem jak to w praktyce by wyglądało;

Mimo wszystko dobrze by było żeby wytłumaczył Ci tę tabelę nauczyciel i jak będziesz dokładnie wiedział jak się nią posługiwać - wtedy zabierz się za program.

0

Witam, już wiem jak ma działać tabela.

Jeżeli ktoś będzie miał taki problem jak ja to poniżej ma odpowiedź:

Popatrzmy na pierwszych 3 zawodników z góry. Nr 2 gra z zawodnikiem nr 18 lub 15 (wiadomo, zależy czy 18 wygra z 15 czy na odwrót). Jeżeli 18 przegra z 15, to nr 18 ląduje w tabeli drugiej, pod numerem -1 (i gra z nr -12). Wtedy nr 2 z tabeli pierwszej gra z numerem 15 i przegrany ląduje w tabeli nr 2 pod numerem -9.

Ale nie o to mi chodzi. :)

Przychodzę tu z następującym problemem:
Imiona i nazwiska na tabeli zamieszczam jako Label'e od 1 do 48 (tyle Labeli musi być na pierwszej tabeli).
Napisałem procedurę, która ma zapisywać Labele do tablicy typu TLabel a następnie do pliku binarnego... i procedurę która odczytuje wartość pliku do tablicy a następnie przypisuje jej wartosci do Labeli. Kod wygląda następująco:

{PROCEDURA ZAPISUJACA}
procedure TForm1.ZapiszAktualnyTurniejClick(Sender: TObject);
var
  tab : array[1..100] of TLabel;
  i   : integer;
  plik: file of TLabel;
begin
  { WCZYTYWANIE LABELI DO TABLICY  }
  for i:=1 to 49 do      // od 1 do 49 bo jeden label to nazwa turnieju.
        if (Form1.FindComponent('Label'+inttostr(i)).className='TLabel')
          then
            begin
              tab[i]:=(Form1.FindComponent('Label'+inttostr(i)) as TLabel);
              if i=49 then showmessage('Zapis udany');
            end;

  AssignFile(plik,'aktualny.bin');
  rewrite(plik);
  for i:=0 to 48 do
    begin
    seek(plik,i);
    write(plik, tab[i+1]);
    end;
  closefile(plik);

end;

{PROCEDURA WCZYTAJ}
procedure TForm1.Wczytajturniej1Click(Sender: TObject);
var
  tab : array[1..100] of TLabel;
  i   : integer;
  plik: file of TLabel;

begin
  AssignFile(plik,'aktualny.bin');
  reset(plik);
  for i:=0 to 48 do
    begin
    seek(plik,i);
    read(plik, tab[i+1]);
    end;

  closefile(plik);
  i:=1;
  for i:=1 to 49 do
            begin
              (Form1.FindComponent('Label'+inttostr(i)) as TLabel).Caption:=tab[i].Caption;
              if i=49 then showmessage('Wczytano poprawnie');
            end;
end;

Po zapisaniu wyskakuje showmessage wczytano poprawnie. Po odczycie rowniez wyskakuje showmessage, ale Labele nie zmieniaja swojej wartosci Caption.

Gdzie popelnilem blad? Zastanawialem sie nad wskaznikiem seek czy dobrze go ustawiam ale wydaje mi sie ze jest gitara.

Po uruchomieniu programu i odpaleniu procedury wczytujacej wyskakuje blad:

---------------------------
Debugger Exception Notification
---------------------------
Project Project1.exe raised exception class EAccessViolation with message 'Access violation at address 00434515 in module 'Project1.exe'. Read of address 01D43ECC'. Process stopped. Use Step or Run to continue.
---------------------------
OK   Help   
---------------------------

Prosze o pomoc ;p

0

Myśl co robisz. Najpierw poczytać powinieneś kursy z podstawami. Tak się nie zapisuje komponentów przede wszystkim. Poza tym jeżeli te Labele w takiej ilości nie są generowane i rozmieszczane dynamicznie, to gratuluje pomyślunku. Ciekawe jeśi bylo by ich 5000 to też byś je mozolnie układał na formatce i później poprawiał coś? Przede wszystkim do odczytu lub zapisu plików wykorzystaj klasę TFileStream lub TMemoryStrem jeżeli już piszesz pod VCL. Zapisuj sobie albo ograniczony długościowo w deklaracji typ string albo zapisuj przed każdym stringiem jego długość i później to odczytuj.

Poniżej prosty przykład dla operacji na konstrukcjach dla Pascala. Podobnie trzeba pokombinować ze wspomnianymi klasami do przechowywania plików lub danych z pliku i pamięci. Przykłady dla Obiekt pascala bez VCL, kolejno pierwszy z ograniczeniem długości stringa i drugi z zapisem długości stringów do pliku.

program example1;

{$APPTYPE CONSOLE}

type
  TRec = record
    Cnt : Byte;
    Txt : string[20];
  end;
const
  AFileName = 'D:\test.bin';
  TxtArr : array[1..2] of string = ('raz', 'dwa');
var
  I : integer;
  AFile : file of TRec;
  DataArr : array[1..2] of TRec;
begin
  for I := Low(DataArr) to High(DataArr) do
  begin
    with DataArr[I] do
    begin
      Cnt := I;
      Txt := TxtArr[I];
    end;
  end;
  // Zapis
  Assign(AFile, AFileName);
  Rewrite(AFile);
  for I := Low(DataArr) to High(DataArr) do
    Write(AFile, DataArr[I]);
  Close(AFile);
  // Odczyt
  Assign(AFile, AFileName);
  Reset(AFile);
  for I := Low(DataArr) to High(DataArr) do
  begin
    Writeln('Rekord numer: ', I);
    with DataArr[I] do
    begin
      Writeln('Pole Cnt: ', I);
      Writeln('Pole Txt: ', Txt);
    end;
  end;
  Writeln('Rozmiar pliku (ilosc rekordow): ', FileSize(AFile));
  Close(AFile);
  Readln;
end.
program example2;

{$APPTYPE CONSOLE}

const
  MaxCnt = 6;
  AFileName = 'D:\test_2.bin';
  TxtArr : array[1..MaxCnt] of string = ('ucz', 'sie', 'podstaw', 'i', 'kombinuj', 'sam.');
var
  S : string;
  AFile : File of Byte;
  I, TxtLength, RecCnt : integer;
  DataArr : array[1..MaxCnt] of string;
begin
  for I := Low(DataArr) to High(DataArr) do
    DataArr[I] := TxtArr[I];
  // Zapis
  RecCnt := Length(DataArr);
  Assign(AFile, AFileName);
  Rewrite(AFile);
  BlockWrite(AFile, RecCnt, SizeOf(RecCnt));
  for I := Low(DataArr) to High(DataArr) do
  begin
    TxtLength := Length(DataArr[I]);
    BlockWrite(AFile, TxtLength, SizeOf(TxtLength));
    BlockWrite(AFile, DataArr[I][1], TxtLength);
  end;
  Close(AFile);
  // Odczyt
  Assign(AFile, AFileName);
  Reset(AFile);
  BlockRead(AFile, RecCnt, SizeOf(RecCnt));
  for I := 1 to RecCnt do
  begin
    BlockRead(AFile, TxtLength, SizeOf(TxtLength));
    SetLength(S, TxtLength);
    BlockRead(AFile, S[1], TxtLength);
    Writeln(S);
  end;
  Close(AFile);
  Readln;
end.

Przeanalizuj sobie też kod dołączony do tego posta. To zmodyfikowany kod, który kiedyś jako przykład napisał tutaj i umieścił @Adam Boduch. Wiem, że kod nie jest najbardziej elegancki, ale powinieneś zrozumieć ideę posługiwania się strumieniami pod VĆL. I proszę na przyszłośc używaj zainstalowanego pomiędzy swoimi uszami TBrain, googluj, myśl jak najwięcej, kombinuj sam, a na forum pisz w ostateczności. Rozumiem, że to dział Newbie, ale nawet to nie zwalnia z pogooglowania i poczytania kursów lub dokumentacji zanim się coś tak głupiego zacznie tworzyć jak jakiś File of TLabel.

0

Nieco rozbawił mnie ten kod, @mthw :]

Wiesz co oznacza ta linijka:

tab : array[1..100] of TLabel;

Zadeklarowałeś macierz stu elementów - niby komponentów - jednak jest to po prostu tablica 100 zwykłych pointerów; Nie możesz w ten sposób robić... Musisz zapisywać poszczególne elementy komponentu, jak np. Caption (w tym przypadku dodatkowo długość łańcucha żeby było wiadomo ile znaków zapisać);

Układanie ręcznie tylu komponentów na formularzu nie jest najlepszym rozwiązaniem - polecam tworzyć je dynamicznie, a w pliku z danymi tabelki zapisać nie tylko tekst etykiety, ale także położenie komponentu na formularzu - dzięki temu będziesz mógł wczytywać informacje o kolejnych TLabel, tworzyć je dynamicznie, nadawać im odpowiedni tekst (Caption) oraz układać je w odpowiednim miejscu na formularzu; No i skorzystać np. z klasy TFileStream tak, jak wspomniał @olesio;

Poza tym jeśli odczytujesz dane z pliku procedurą Read to nie musisz dodatkowo używać Seek - pozycja kursora jest automatycznie zwiększana; Podobnie jest przy odczycie danych ze strumieni;

No i nie wiem po co tworzysz tablicę stu etykiet, skoro wszystkich ich nie wykorzystujesz...

Polecam przytulić jakiś kurs programowania w Pascalu (i Object Pascalu) i nieco poczytać, zanim zaczniesz się bawić w jakiekolwiek "większe" aplikacje; Bez znajomości podstaw w kółko będziesz miał problemy.

0

Nie oczekuj ze kod bedzie idealny (tablica specjalnie zadeklarowana 100 zeby przy zabawie miejsca nie zabraklo).

Faktycznie, z tym file of TLabel przesadzilem.

Nie widze sensu tworzenia dynamicznie Labeli. Przeciez jak je nawrzucam myszka a potem bede chcial je przesuwac lub wykonywac inne operacje to mam dostep do Label1.Left itp. A nie bedzie ich 5000...

Moze wytlumacze o co w programie chodzi. W pierwszym poscie jest zalacznik, ktory przerysowalem. Gdybym tworzyl labele dynamicznie to musialbym sie naglowkowac z obliczeniami (nie sa rozstawione w tej samej odleglosci!). Wole wrzucic je myszka i zmieniac ich wartosci Caption. Jezeli Label1 przegra z Label2 to Label3 przyjmie wartosc Label2 itd...

Proboje zrobic save'a tych pozycji, tak aby mozna bylo zapisac turniej do pliku i go wczytac po wlaczeniu ponownie programu.

Chce przypisac wartosci Caption do Stringa z typu. Na osobnej formie wpisuje w Editach 24 zawodnikow -> Zastosuj -> Label1.Caption:=Edit1.Text;...Label24.Caption:=Edit24.Text; -> OK, działa. Potem stosujac sie do wskazowek powyzszych robie tak:

for i:=Low(tab) to High(tab) do
    begin
      with Tab[i] do
        Nazwa   :=  (Findcomponent('Label'+IntToStr(i)).Caption); //i tutaj jest problem.
        Dlugosc :=  Length(Form1.Findcomponent('Label'+IntToStr(i).className='TLabel'));
    end;

Nie jestem super programista ale program napisac musze i staram sie. Dlatego zrobilem tablice typu TLabel zeby zapisac ja do pliku i potem to samo wczytywac... Nie byloby problemu ze stringiem... Chce zrobic to w petli dlatego jest problem, bo moglbym przeciez kazdego labela do kazdego miejsca tablicy wpisywac ale to bez sensu.

0

Eee? Ludzie powyżej wytłumaczyli Ci czemu nie możesz zrobić file of TLabel a ty dalej się przy tym upierasz to raz. Dwa: Czy jak byś zrobił tablice Labeli to czym by zapis Label[1].Left różnił się od Label1.Left?
No i oczywiście zamiast pisać

for i:=Low(tab) to High(tab) do
    begin
      with Tab[i] do
        Nazwa   :=  (Findcomponent('Label'+IntToStr(i)).Caption); //i tutaj jest problem.
        Dlugosc :=  Length(Form1.Findcomponent('Label'+IntToStr(i).className='TLabel'));
    end;

Zawsze można napisać

for i:=Low(tab) to High(tab) do
    begin
      with Tab[i] do
        Nazwa   :=  Label[i].Caption; //i tutaj jest problem.
        Dlugosc :=  Length(Label[i].Caption);
    end;

Co pod względem wydajności jest lepsze niż Twoje rozwiązanie.

Ale po co. Przecież mogę zrobić 100 labeli z czego użyje tylko 50, wklikać je na fromatkę a następnie płakać na forum że nie działa albo nie umiem.

0
mthw napisał(a)

Nie widze sensu tworzenia dynamicznie Labeli.

To teraz wyobraź sobie taką sytuację, że Twój program ma być używany na różnych zawodach z różną ilością zawodników; Do tego celu musisz odpowiednio zmienić tabelę (z mniejszą lub większą ilością miejcs), bo przecież dla 24 zawodników nie potrzeba tabeli z 64 miejscami; Myślisz, że wyklikanie telu etykiet będzie dobre? Uniwersalne? Nie - więcej przysporzy problemów niż pożytku;

mthw napisał(a)

Gdybym tworzyl labele dynamicznie to musialbym sie naglowkowac z obliczeniami (nie sa rozstawione w tej samej odleglosci!).

Czyli nie przeczytałeś ze zrozumieniem mojego poprzedniego postu;

Nie musisz nic obliczać - możesz poukładać sobie etykiety na formularzu, ale tylko po to, by odczytać współrzędne ich położenia; Te współrzędne jak i treść etykiety zapisujesz do pliku w następującej sekwencji:

| 1 bajt        | 1 bajt          | N bajtów  | 2 bajty | 2 bajty | 1 bajt          | ...
| ilość etykiet | długość Caption | Caption   | Left    | Top     | długość Caption | ...

Ilość etykiet może być wpisana na początku - przyda się do ustawienia pętli, ale nie musi - można odczytywać aż do końca pliku;

Teraz odczytujesz po kolei dane wszystkich etykiet, tworzysz je dynamicznie pakując ich referencję do dynamicznej macierzy o długości takiej, jaka jest ilość etykiet w pliku (pierwszy bajt); Następnie odczytujesz ich położenie takie im ustawiasz, na koniec treść etykiety i gotowe; Masz utworzone wszystkie etykiety dynamicznie (dlatego nie marnuje się pamięci), nic nie musisz już klikać/ukrywać/pokazywać - wszystko dzieje się dynamicznie; Do tego pliku możesz zapisać także dodatkowe informacje, takie jak np. treść podpowiedzi (właściwiść Hint komponentu) i co tylko innego chcesz; Całość pakujesz do pliku amorficznego i odczytujesz wykorzystując np. TFileStream;

mthw napisał(a)

Proboje zrobic save'a tych pozycji, tak aby mozna bylo zapisac turniej do pliku i go wczytac po wlaczeniu ponownie programu.

Wykorzystując mój pomysł mógłbyś zapisywać i dane komponentów, i dodatkowe informacje o sesji do jednego pliku; Albo zapis sesji wykonać do osobnego pliku - tylko dodatkowe dane dla odpowiednich etykiet; Wtedy zamiast tworzyć macierz etykiet stworzyłbyś sobie macierz struktur z referencją do etykiety i jej dodatkowymi informacjami (danymi sesji); Przykład podał Ci @olesio, ale w tym przypadku nie mógłbyć skorzystać z plików typowanych, bo co prawda dodatkowe dane zostałyby zapisane, ale dane komponentu nie - zapisany zostałby adres komponentu (tak, jak robiłeś wcześniej); W strukturach przechowywałbyś tylko informacje, a zapis już byłby do pliku amorficznego; Proste - trzeba tylko ewentualnie poczytać, jeśli wiedzy brakuje.

0
babubabu napisał(a):

Eee? Ludzie powyżej wytłumaczyli Ci czemu nie możesz zrobić file of TLabel a ty dalej się przy tym upierasz to raz. Dwa: Czy jak byś zrobił tablice Labeli to czym by zapis Label[1].Left różnił się od Label1.Left?
No i oczywiście zamiast pisać

for i:=Low(tab) to High(tab) do
    begin
      with Tab[i] do
        Nazwa   :=  (Findcomponent('Label'+IntToStr(i)).Caption); //i tutaj jest problem.
        Dlugosc :=  Length(Form1.Findcomponent('Label'+IntToStr(i).className='TLabel'));
    end;

Zawsze można napisać

        Nazwa   :=  Label[i].Caption; //i tutaj jest problem.

Taki zapis nie działa...

Nie mam juz tablicy TLabel.

type
  ...
  TRec = record
    nazwa   : string[30];
    dlugosc : byte;
  end;

const
  AFileName = 'save.bin';

var
  Form1 : TForm1;
  tab   : array[1..49] of TRec;
  Plik  : TFileStream;
0

Więc skoro korzystasz z macierzy struktur to sugeruj się kodem podanym przez @olesio - napisał Ci wszystko w tym temacie (reszta w dokumentacjach i Google).

1 użytkowników online, w tym zalogowanych: 0, gości: 1