Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » Tetris für Anfänger?

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 <
000
13.02.2005, 18:56 Uhr
inwest



Zuerst mal hallo, bin neu hier.

Und nicht nur neu hier, sondern auch neu in C Plus Plus. Ich hab' mich mal mit den Grundlagen vertraut gemacht. Das heisst, ich beherrsche mal:

- Struktur des Quellcodes
- Ausgaben
- Eingaben
- Schleifen (for, while)
- Variabeln deklarieren
- Operatoren
- STL (ausser sort())
- Klassen (public, private, protected)
- Datentypen (zumindest fünf bis zehn)


Man sieht also, ich bin ein Anfänger, habe mich aber schon über die ersten Hürden gewagt.

Nun möchte ich meine Fähigkeiten erweitern, und ich dachte mir, ein simples Tetris programmieren wär' doch was. Ich dachte zuerst an einen Toolkit. Zum Beispiel GTK, SDL, Curses oder Dialog. Aber nachdem ich ein bisschen drüber nachgedacht habe war es mir plötzlich lieber, ohne grossen "schnickschnack" was zu machen. Das heisst also quasi ein ASCII-Tetris. Nur mit Buchstaben. Also würden einige Tetris-Symbole als Beispiel so aussehen:

Querbalken:
x x x x

"L"-Baustein:
x
x
x x

Das ganze vielleicht mit verschiedenen Farben, um die Bausteine auseinander zu halten. Ich hab' mir dann überlegt, wie man sowas programmieren könnte. Bisher habe ich nur kleine, nicht-interaktive (oder nur à la Namen-eingeben) - Progrämmchen geschrieben. Zum Beispiel: Gib' Zahl1 und Zahl2 ein und ich rechne Dir Ergebnis1.

Aber, wie bringt man denn überhaupt einen "Baustein zum fallen"? Wie nehme ich Signale von der Tastatur entgegen, und wie kann ich programmieren: if links-drücken then bewege den Balken nach links? Es klingt dumm, aber ich stand plötzlich im leeren. In meinen Büchern steht meistens nur "Mathekram" oder irgendwelche "Sinn und nicht-Sinn von OO-Programmierung", aber nirgends "Wie schreibe ich interaktive Programme". Ich habe mich dann auf die Suche begeben, aber ich wusste eigentlich gar nicht, nach was ich suchen soll. Wie nennt man denn das, was ich machen will, überhaupt? In meiner Ratlosigkeit suchte ich nach "ascii tetris", und tatsächlich hab' ich was gefunden. Das war aber leider allerdings in C programmiert, mit so vielen C-Libraries und Structs, dass ich gar keine Lust mehr hatte, mich da irgendwie weiter hinein zu steigern.

Nun möchte ich euch fragen, ob ihr evtl. Tipps, Links oder ähnliches auf Lager habt.

Gruss und vielen Dank.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
13.02.2005, 19:34 Uhr
0xdeadbeef
Gott
(Operator)


Hui, da haste dir schon was hübsches vorgenommen. Allerdings auch was nicht ganz einfaches.

Du hast im Grunde zwei große Probleme zu bewältigen. Das erste ist, dass die Eingabe normalerweise gepuffert ist. Zum Beispiel wird

C++:
#include <iostream>
int main() { std::cin.get(); }


solange warten, bis der Stream geflusht wird (also, bis du Enter drückst). Du allerdings willst die Eingabe entpuffern und die key-events haben, sobald die Taste gedrückt wird. Dummerweise gibt es dafür auf der Konsole keinen portablen Weg. Naja - es gibt schon einen Standard, in dem das enthalten ist (nennt sich POSIX), aber da Microsoft sich nicht daran hält, ist es mit der Portabilität de facto nicht weit her. Unter Windows würdest du also im Zweifel auf das alte DOS-API zurückgreifen (#include <conio.h> ), auf allen anderen Plattformen vermutlich ncurses (ncurses ist sowas wie ein Textmodus-Windowing-Toolkit).

Das zweite, und komplizierter zu begreifende Problem ist der parallele Ablauf zweier Programmteile. Du willst zum einen die Steine fallen lassen, zum anderen Usereingaben verarbeiten. Während du auf neue Usereingaben wartest, sollen die Steine aber trotzdem weiterfallen. Mit anderen Worten, die beiden Programmteile müssen parallel laufen, und das nennt man dann Multithreading. Naja - eigentlich nennt man es Multitasking, aber in diesem Fall ist die simpelste Möglichkeit, den Kram zu lösen, über Threads, also Multithreading. Threads sind zwar eigentlich auch vom Betriebssystem abhängig - aber dafür gibts ja boost. Schau mal hier vorbei: www.boost.org/doc/html/threads.html - das ist ein ziemlich hübsches API für Threads. Threads parallel ablaufen zu lassen ist ziemlich simpel, sie das machen zu lassen, was sie machen sollen, ist aber nicht ganz so einfach. Das Problem liegt darin, dass die beiden Threads zum Teil die selben Ressourcen benutzen - in diesem Fall das Spielfeld. Wenn beide Threads gleichzeitig am Spielfeld rumbasteln, kommt im Zweifel nachher ziemlicher Müll raus, und das gilt es zu vermeiden. Dafür gibt es das Konzept des Mutex (mutually exclusive). Läuft darauf hinaus, dass ein Thread nur dann Zugriff auf eine Ressource erhält, wenn die bisher nicht belegt ist. (ein Mutex ist im Grunde ein Spezialfall eines Semaphors). Auch dafür hat die boost-Thread-library ein paar hübsche Klassen bereitgestellt (für deinen Fall solltest du dir mal die locks ankucken).
--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra

Dieser Post wurde am 13.02.2005 um 19:34 Uhr von 0xdeadbeef editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
13.02.2005, 19:41 Uhr
firefoxxi



Wenn du das ganze nur für die Console machen willst dann wird das nicht mit ANSI C++ gehen da du Windows spezifische Funktionen benutzen musst. Wenn du das ganze mit Windows machen willst kannst du zur Abfrage die Funktion getch() benutzen welche sich in der conio.h befindet diese verlangt ein einzelnes Zeichen und kann auch beispielsweiße die Pfeiltasten lesen. Gib dafür spezielle Tastencodes die du selbst rausfinden musst. Allerdings ist hierbei das Problem das das Programm immer steht bis der User eine Taste gedrückt hat. Eine andere Funktion fällt mit gerade nicht ein. Damit die Steine nicht zu schnell fallen kannst du eine Schleife machen die in etwa so aussieht:


C++:
while(1) {
    int time = time()
    // Dein Code
    while(time + 5000/*<-- Wartezeit*/==time()) {}
}



Wobei time() eine Funktion sein sollte die Zeit in Millisekunden zurücklifert (kenn leider keine auswendig)
--
Gruß
firefoxxi

http://firebird-browser.de/
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
13.02.2005, 19:43 Uhr
firefoxxi



Mist 0xdeadbeef war erster!
--
Gruß
firefoxxi

http://firebird-browser.de/
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
13.02.2005, 22:10 Uhr
inwest



Hallo!

Zuerst mal vielen Dank für die ausführlichen Antworten. Ich werde mir eure Tipps und Links genauer anschauen. Aber vorab: Ich arbeite nicht mit Windows, sondern mit Debian GNU/Linux Sarge. Ich bin also nicht auf Win-Libraries angewiesen, sondern bediene mich am liebsten an freien Libraries. Ich dachte ich sollte euch das noch sagen.


Zitat:
Du hast im Grunde zwei große Probleme zu bewältigen. Das erste ist, dass die Eingabe normalerweise gepuffert ist. Zum Beispiel wird


C++:
#include <iostream>
int main() { std::cin.get(); }



solange warten, bis der Stream geflusht wird (also, bis du Enter drückst). Du allerdings willst die Eingabe entpuffern und die key-events haben, sobald die Taste gedrückt wird. Dummerweise gibt es dafür auf der Konsole keinen portablen Weg. Naja - es gibt schon einen Standard, in dem das enthalten ist (nennt sich POSIX), aber da Microsoft sich nicht daran hält, ist es mit der Portabilität de facto nicht weit her.


... hm, dann bin ich froh, dass ich nach POSIX Standard Definition programmiere. Ich denke, dann wird auch Deine Antwort/Hilfe anders lauten.


Bis später!

Dieser Post wurde am 13.02.2005 um 22:13 Uhr von inwest editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
005
14.02.2005, 01:14 Uhr
Pablo
Supertux
(Operator)


wenn du POSIX programmierst, dann nimm ncurses, das ist eine tolle Lib.
--
A! Elbereth Gilthoniel!
silivren penna míriel
o menel aglar elenath,
Gilthoniel, A! Elbereth!
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
006
14.02.2005, 02:15 Uhr
0xdeadbeef
Gott
(Operator)


Juhu - ein Anfänger, der gleich Linux benutzt! Mann, wann hab ich das das letzte mal unter meinen Fingern gehabt?

Wie auch immer. Ich nehme an, dass besonders der Thread-Kram ne ganze Menge Fragen aufwirft. Das Prinzip ist eigentlich nicht besonders kompliziert, aber ich schlage trotzdem vor, dass du dir ein bisschen Theorie dazu anliest - vielleicht kriegst du irgendwelche Universitätsskripte in die Hand (bei uns hieß die entsprechende Vorlesung Betriebssysteme und Netze, vielleicht hilft das, was sinnvolles zu finden), jedenfalls solltest du die Begriffe "Semaphor", "Mutex" und "Thread" verstehen, bevor du mit dem Kram anfängst. Danach würde ich mir an deiner Stelle die boost.thread-library, die ich weiter oben schon verlinkt habe, ankucken - die ist für diese Dinge geradezu unglaublich praktisch. ncurses dagegen ist relativ einfach - im Grunde folgt es den selben Gesetzen wie herkömmliche Grafiktoolkits. Zu beiden Geschichten solltest du dir aber erstmal was anlesen (sollte mit google nicht schwer zu finden sein). Ich kann dir zwar auf Detailfragen (a la "warum funzt der Code so nicht?") beantworten, aber es wäre deutlich weniger nervtötend für mich und andere, wenn du schon einen groben Überblick über die Materie hast. Ich kann dir allerdings ein kleines Boost.Thread-Beispiel geben, das ich mal zusammengeschustert habe:

C++:
#include <boost/thread/thread.hpp>
#include <boost/thread/xtime.hpp>

#include <iostream>

class timer_thread {
public:
  timer_thread(bool &interrupt) : m_interrupted(interrupt) { }
  void operator()() const {
    while(!m_interrupted) {
      std::cout << "timer_thread" << std::endl;

      boost::xtime xt;
      boost::xtime_get(&xt, boost::TIME_UTC);
      ++xt.sec;
      ::boost::thread::sleep(xt);
    }
  }

private:
  bool &m_interrupted;
};

class control_thread {
public:
  control_thread(bool &int_timer) : m_interrupt_timer_thread(int_timer) { }
  void operator()() {
    for(int i = 0; i < 5; ++i) {
      std::cout << "control_thread" << std::endl;

      boost::xtime xt;
      boost::xtime_get(&xt, boost::TIME_UTC);
      xt.sec += 5;
      ::boost::thread::sleep(xt);
    }

    m_interrupt_timer_thread = true;
  }

private:
  bool &m_interrupt_timer_thread;
};

int main() {
  bool interrupt = false;
  timer_thread   timer_th_fctor(interrupt);
  control_thread ctl_th_fctor  (interrupt);

  std::cout << "main begin" << std::endl;

  boost::thread timer_th  (timer_th_fctor);
  boost::thread control_th(ctl_th_fctor  );

  timer_th.join();
  control_th.join();

  std::cout << "main end" << std::endl;
}


timer_th und control_th laufen halt parallel ab. Sollte nicht zu schwer zu blicken sein, denke ich - und ansonsten frag halt.

Oh, und noch eine Sache - wenn du nachher mit dem eigentlichen Coden anfängst, denk an die Modularität. In diesem Fall insbesondere daran, dass du die Darstellung möglichst sauber vom Rest abtrennst, damit du den Kram nachher möglichst einfach auf grafische Toolkits portieren kannst - das wäre dann nämlich so ziemlich der nächste logische Schritt. (Ich würde dir raten, gtkmm zu benutzen, das ist schön c++ig.)
--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
007
14.02.2005, 02:40 Uhr
Pablo
Supertux
(Operator)


beffy, glaubst du wirklich, dass boost wirklich für einen Anfänger geeigent ist?
--
A! Elbereth Gilthoniel!
silivren penna míriel
o menel aglar elenath,
Gilthoniel, A! Elbereth!
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 <     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: