Aufgabe 5: Nach der Party
Vorüberlegungen
Eigentlich braucht das Programm nur bei jeder bei neuen Festlegung einer Bekanntschaft zu überprüfen, ob ein Partner Kenntnis von Katrins Fest besitzt. Ist dies der Fall, so kennt ab sofort auch der andere Partner das Fest. Das einzige Problem besteht darin, Robert als Partner abzufangen, da er niemandem weitererzählen wird.
Auch muß Wissen weitergetragen werden, so daß alle Freunde dieser Bekanntschaften in eventuelle Änderungen bezüglich des Fest involviert werden.
Bedienung
In der oberen linken Ecke gibt man einen neuen Namen ein, der durch den Button rechts daneben zu den beiden oberen Listen hinzugefügt wird.
Um eine Bekanntschaft festzulegen, klickt man die Namen der beiden an, einen in der linken, den anderen in der rechten Liste. Abschließend drückt man den kennt-Button. Jede Bekanntschaft ist in der unteren linken Liste nachzulesen.
In der unteren rechten Liste werden alle Freunde der zuletzt selektierten Person angezeigt.
Zur einfachen Bestimmung, wer alles Katrins Fest kennt, erhält jeder Wissende ein kleines Kreuz zum Namen hinzu. Allerdings gilt dies nur für die untere rechte Liste.
In der Dialogmitte schließlich steht, wie oft Robert von dem Fest hört.
Bei Programmstart sind die Namen Katrin und Robert bereits eingetragen, wobei Katrin natürlich Kenntnis von ihrem Fest besitzt.
Jeder Gast ist als Freund Katrins einzugeben und die Anzahl aller Personen auf 100 (siehe MaxPersonen) zu beschränken. Es werden keinerlei wiederholte Eingaben mit gleichem Inhalt abgefangen, z.B. doppelte Eingabe des Namens Hans. Dies trifft sowohl auf die Namen (inkl. Robert und Katrin), als auch auf die Bekanntschaftsbeziehungen zu.
Dokumentation
Bei diesem Programm teilen sich zwei Methoden die Hauptarbeit. Da sie auch Aufgaben zur Bildschirmkommunikation erfüllen, beschreibe ich jetzt nur die zur Aufgabenlösung wesentlichen Bestandteile folgender Funktionen:
void OnKnows();
void UpdateAlleBekanntschaften();
OnKnows
Diese Methode wird jedesmal aufgerufen, wenn eine neue Bekanntschaft definiert wird
Zuerst werden die numerischen Werte des Selektionen in der linken und der rechten oberen Liste bestimmt. Dabei werden zwei Sonderfälle ausgeschlossen:
Letzterer Punkt ist zwar inhaltlich nicht richtig, beschreibt aber, daß Katrin Robert nie von dem Fest erzählen würde.
Nachdem die Bekanntschaft in der unteren linken Liste eingetragen wurde, erhält das Bekannschaften-Feld einen Eintrag. Sofort wird UpdateAlleBekanntschaften aufgerufen, um mögliches Wissen über das Fest zu verbreiten. Am Schluß wird noch die Liste der Einzelbekanntschaften aktualisiert.
UpdateAlleBekanntschaften
Diese Methode ist alleinig für das Verknüpfen unterschiedlicher Freundschaften und des entsprechenden Wissens über das Fest zuständig.
Dazu wird gleich zu Beginn eine boolesche Variable deklariert, welche jede, noch so geringe, Änderung an einem der beiden Felder Bekanntschaften und KenntFest feststellt. Solange sie wahr ist, wird die Schleife immer wieder durchlaufen. Damit ein vollständiges Bearbeiten abgesichert.
Jeder Durchlauf überprüft, ob ein Bekanntschaftsverhältnis zu einer anderen Person vorliegt. Ist dies nicht der Fall, so wird das Verhältnis der umgekehrten Richtung übernommen. Grund dafür ist, das die Kombination (a;b) als sich fremd deklariert sein kann, dagegen (b;a) sich kennt.
Danach erfolgt der Test, ob das Fest bekannt ist. Bei Verneinung wird das Wissen des Freundes übernommen. Allerdings muß Robert als Freund ausgeschlossen werden, da er niemandem von Katrins Fest erzählt.
Zuletzt erfolgt eine Aktualisierung des Bildschirms.
Beispiele
Für das vorgegebene Beispiel ergibt sich, daß Robert von 7 Personen erfährt, wie toll das Fest war. Dazu gehören: Jochen, Muriel, Cornelia, Peter, Michael, Herbert und Janine.
Robert erfährt somit 1mal von dem Fest. Interessant an dieser Konstellation ist die Reihenfolge. Gibt man erst die Bekanntschaft Christian-Robert, danach Christian-Stephan und zuletzt Stephan-Katrin, so muß das Programm sich rückwärts von Katrin über Stephan und Christian zu Robert vorarbeiten.
Robert erfährt 3mal von dem Fest.
Besonders das erste und das dritte Beispiel zeigen, daß Robert nicht von jedem seiner Freunde erzählt bekommt, was für ein tolles Fest Katrin organisiert hat. Dadurch konnte ich auch schnell das Problem erkennen, daß Robert als einziger niemanden sein Wissen weitererzählen darf.
Quelltext
FUENF.H
#include "resource.h"
class CFuenfApp : public CWinApp // Basisklasse
{
public:
virtual BOOL InitInstance(); // Start überladen
};
#define KatrinIndex 0 // Katrins Index
#define RobertIndex 1 // Roberts Index
#define MaxPersonen 100
class CFuenfDlg : public CDialog // Dialogklasse
{
public:
CFuenfDlg(CWnd *pParent = NULL); // Konstruktor
enum { IDD = ID_DIALOG }; // ID aus der Resource
protected:
virtual BOOL OnInitDialog(); // Dialoginitialisierung
afx_msg void OnOK(); // Entertaste
afx_msg void OnAdd(); // Namen hinzufügen
afx_msg void OnKnows(); // kennt-Button
afx_msg void OnLinks(); // Focus auf linke Liste
afx_msg void OnRechts(); // Focus auf rechte Liste
afx_msg void OnAbout(); // Programminfo
afx_msg void OnClose(); // Programm beenden
private:
void UpdateEinzelneBekanntschaften(); // einzelne Bekanntschaften anzeigen
void UpdateAlleBekanntschaften(); // alle Bekanntschaften verknüpfen
void UpdateRobert(); // wie oft Robert vom Fest erfährt
void AddToListBoxLinksRechts(LPCSTR cName, BOOL bFest);
// Namen zu beiden oberen Listen hinzufügen
void GetName(int nIndex, char* cName); // Namen holen
void GetMarkedName(int nIndex, char* cName);
// wie GetName, nur mit Markierung
int nFocus; // zuletzt markierter Name
int nPersonen; // Anzahl Personen
BOOL Bekanntschaften[MaxPersonen][MaxPersonen];
// alle Bekanntschaften
BOOL KenntFest[MaxPersonen]; // vom Fest Wissende
DECLARE_MESSAGE_MAP() // Messagehandler installieren
};
FUENF.CPP
#include "afxwin.h" // MFC-Definitionen
#include "fuenf.h" // Headerfile einbinden
CFuenfApp FuenfApp; // Programm statisch erzeugen
virtual BOOL CFuenfApp::InitInstance() // Start überladen
{
Enable3dControls(); // 3D-Stil
CFuenfDlg dlg; // Dialog statisch erzeugen
m_pMainWnd = &dlg; // Zeiger darauf eintragen
dlg.DoModal(); // modal ausführen
return FALSE; // fehlerfrei
}
BEGIN_MESSAGE_MAP(CFuenfDlg, CDialog) // Messages weiterleiten
ON_BN_CLICKED(ID_BUTTON_HINZU, OnAdd)
ON_BN_CLICKED(ID_BUTTON_KENNT, OnKnows)
ON_BN_CLICKED(ID_BUTTON_ABOUT, OnAbout)
ON_LBN_SELCHANGE(ID_LISTBOX_KENNT_LINKS, OnLinks)
ON_LBN_SELCHANGE(ID_LISTBOX_KENNT_RECHTS, OnRechts)
ON_WM_CLOSE()
END_MESSAGE_MAP()
CFuenfDlg::CFuenfDlg(CWnd* pParent):CDialog(IDD, pParent)
// Konstruktor
{
nFocus = ID_LISTBOX_KENNT_LINKS; // Focus auf linke Liste
nPersonen = 0;
memset(Bekanntschaften, FALSE, sizeof(Bekanntschaften));
memset(KenntFest, FALSE, sizeof(KenntFest));
}
BOOL CFuenfDlg::OnInitDialog() // Dialoginitialisierung
{
CDialog::OnInitDialog(); // an Grundklasse weiterleiten
AddToListBoxLinksRechts("Katrin", TRUE); // Katrin einfügen, kennt Fest
AddToListBoxLinksRechts("Robert", FALSE); // Robert einfügen, kennt Fest nicht
((CListBox*)GetDlgItem(ID_LISTBOX_KENNT_LINKS))->SetCurSel(KatrinIndex);
((CListBox*)GetDlgItem(ID_LISTBOX_KENNT_RECHTS))->SetCurSel(KatrinIndex);
// Katrin selektieren
UpdateAlleBekanntschaften();
UpdateEinzelneBekanntschaften(); // Katrins Freunde anzeigen
return TRUE;
}
void CFuenfDlg::OnOK() // Entertaste
{
OnAdd(); // Name zu den Listen hinzufügen
}
void CFuenfDlg::OnAdd() // Namen hinzufügen
{
char cName[50]; // temporär Namen speichern
GetDlgItemText(ID_EDIT_NAMEN, cName, 49); // Namen auslesen
if (cName[0]==0) // falls keiner eingegeben
{
MessageBeep(0); // Piepton, Methode beenden
return;
}
SetDlgItemText(ID_EDIT_NAMEN, ""); // Eingabefeld löschen
AddToListBoxLinksRechts(cName,FALSE); // Name zu Listen hinzufügen
}
void CFuenfDlg::OnKnows() // kennt-Button
{
CListBox* pBoxLinks = (CListBox*)GetDlgItem(ID_LISTBOX_KENNT_LINKS);
CListBox* pBoxRechts = (CListBox*)GetDlgItem(ID_LISTBOX_KENNT_RECHTS);
int nLinks = pBoxLinks->GetCurSel(); // aktuelle Selektion holen
int nRechts = pBoxRechts->GetCurSel();
if ((nLinks==LB_ERR)||(nRechts==LB_ERR)||(nLinks==nRechts))
{ // ungültige Auswahl oder gleicher Name
MessageBeep(0); // Piepton, Methode beenden
return;
}
if (((nLinks==KatrinIndex)&&(nRechts==RobertIndex))||
((nLinks==RobertIndex)&&(nRechts==KatrinIndex)))
{ // Kombination Robert-Katrin ausschließen
MessageBox("Robert und Katrin sprechen nicht miteinander über das Fest.","Problem",MB_OK|MB_ICONSTOP);
return;
}
char cLinks[50], cRechts[50]; // Namen temporär speichern
GetName(nLinks,cLinks); // linker Name
GetName(nRechts,cRechts); // rechter Name
char cWerKenntWen[110]; // Kombination beider Namen
sprintf(cWerKenntWen, "%s kennt %s", cLinks, cRechts);
CListBox* pBox = (CListBox*)GetDlgItem(ID_LISTBOX_ALLE_BEKANNTSCHAFTEN);
pBox->InsertString(-1,cWerKenntWen); // Kombination ans Ende anhängen
Bekanntschaften[nLinks][nRechts] = TRUE;
UpdateAlleBekanntschaften();
UpdateEinzelneBekanntschaften(); // Bildschirmanzeige anpassen
}
void CFuenfDlg::OnLinks() // Focus auf linke Liste
{
nFocus = ID_LISTBOX_KENNT_LINKS; // Focus speichern
UpdateEinzelneBekanntschaften(); // Bildschirmanzeige anpassen
}
void CFuenfDlg::OnRechts() // Focus auf linke Liste
{
nFocus = ID_LISTBOX_KENNT_RECHTS; // Focus speichern
UpdateEinzelneBekanntschaften(); // Bildschirmanzeige anpassen
}
void CFuenfDlg::OnAbout() // Programminfo
{
MessageBox("programmiert von Stephan Brumme\nin Watcom C++ 10.6 mit MFC 3.2",
"Über...", MB_OK|MB_ICONINFORMATION);
}
void CFuenfDlg::OnClose() // Programm beenden
{
if (MessageBox("Wirklick beenden ?","Ende",MB_YESNO|MB_ICONQUESTION)==IDYES)
// Sicherheitsabfrage
CDialog::OnClose();
}
void CFuenfDlg::UpdateEinzelneBekanntschaften() // einzelne Bekanntschaften anzeigen
{
CListBox* pBoxLinksRechts = (CListBox*)GetDlgItem(nFocus);
CListBox* pBoxAlle = (CListBox*)GetDlgItem(ID_LISTBOX_ALLE_BEKANNTSCHAFTEN);
CListBox* pBoxEinzeln = (CListBox*)GetDlgItem(ID_LISTBOX_EINZELNE_BEKANNTSCHAFTEN);
char cName[50], cText[60];
int nName = pBoxLinksRechts->GetCurSel(); // selektiertes Element holen
GetMarkedName(nName, cName); // Namen mit Markierung identifizieren
sprintf(cText, "%s kennt:", cName);
SetDlgItemText(ID_TEXT_EINZELNE_BEKANNTSCHAFTEN, cText);
// Überschrift der Einzelbekanntschaften
// anpassen
pBoxEinzeln->ResetContent(); // Liste löschen
if (pBoxAlle->GetCount()>0) // nur falls bereits Bekanntschaften
for(int Lauf=0; Lauf<MaxPersonen; Lauf++)// alle Bekanntschaften durchlaufen
if (Bekanntschaften[nName][Lauf]) // nur wenn Einzelperson gemeint
{
char cFreund[50];
GetMarkedName(Lauf, cFreund);
// Freund mit Markierung erkennen
pBoxEinzeln->InsertString(-1,cFreund);
// Freund in Liste eintragen
}
}
void CFuenfDlg::UpdateAlleBekanntschaften() // alle Bekanntschaften verknüpfen
{
BOOL bNeu = TRUE; // wahr, wenn neue Bekanntschaften verabeitet
while (bNeu)
{
bNeu = FALSE; // von bearbeitetem Feld ausgehen
for (int X=0; X<nPersonen; X++) // alle Personen durchlaufen
for (int Y=0; Y<nPersonen; Y++) // alle möglichen Freunde
if (X!=Y) // sich selbst ausschließen
{
if (!Bekanntschaften[X][Y]) // noch keine Bekanntschaft ?
if ((Bekanntschaften[X][Y]=Bekanntschaften[Y][X])==TRUE)
bNeu = TRUE; // fast doch, eintragen & neues Feld
if ((Y!=RobertIndex)&&Bekanntschaften[X][Y]&&!KenntFest[X])
// falls nicht Robert der Freund und
// das Fest nicht bekannt
if ((KenntFest[X]=KenntFest[Y])==TRUE)
bNeu = TRUE; // wenn Fest bekannt neues Feld
}
}
UpdateRobert(); // Bildschirmanzeige ändern
}
void CFuenfDlg::UpdateRobert() // wie oft Robert vom Fest erfährt
{
int nRobertWirdErzaehlt = 0; // erstmal Fest unbekannt
for (int Lauf=0; Lauf<nPersonen; Lauf++) // alle Personen
if (Bekanntschaften[RobertIndex][Lauf]&&KenntFest[Lauf])
// wenn Freund und Fest bekannt
nRobertWirdErzaehlt++; // Anzahl Erzählender erhöhen
char cText[50];
sprintf(cText, "Robert wird %imal von dem Fest erzählt.", nRobertWirdErzaehlt);
SetDlgItemText(ID_TEXT_ROBERT, cText); // Anzahl in Bildmitte anzeigen
}
void CFuenfDlg::AddToListBoxLinksRechts(LPCSTR cName, BOOL bFest)
{ // Namen zu beiden oberen Listen hinzufügen
nPersonen++;
((CListBox*)GetDlgItem(ID_LISTBOX_KENNT_LINKS))->InsertString(-1, cName);
((CListBox*)GetDlgItem(ID_LISTBOX_KENNT_RECHTS))->InsertString(-1, cName);
// beide Listen ändern
int nIndex = ((CListBox*)GetDlgItem(ID_LISTBOX_KENNT_LINKS))->GetCount()-1;
KenntFest[nIndex] = bFest; // Festkenntnis speichern
UpdateAlleBekanntschaften();
}
void CFuenfDlg::GetName(int nIndex, char* cName)// Namen holen
{
((CListBox*)GetDlgItem(ID_LISTBOX_KENNT_LINKS))->GetText(nIndex, cName);
}
void CFuenfDlg::GetMarkedName(int nIndex, char* cName)
{ // wie GetName, nur mit Markierung
CListBox* pBox = (CListBox*)GetDlgItem(ID_LISTBOX_KENNT_LINKS);
pBox->GetText(nIndex, cName);
if (KenntFest[nIndex])
sprintf(cName, "%s*", cName); // bei Festkenntnis Stern anhängen
}
FUENF.DLG
100 DIALOG FIXED IMPURE 27, 14, 231, 175
STYLE DS_MODALFRAME | DS_3DLOOK | WS_OVERLAPPED | WS_CAPTION | WS_VISIBLE | WS_SYSMENU
CAPTION "Aufgabe 5"
FONT 8, "Helv"
BEGIN
CONTROL "", 101, "EDIT", ES_LEFT | ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 10, 10, 75, 12
CONTROL "&Hinzu", 102, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 95, 10, 40, 14
CONTROL "", 103, "LISTBOX", LBS_STANDARD | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | WS_TABSTOP, 10, 30, 75, 66
CONTROL "", 105, "LISTBOX", LBS_STANDARD | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | WS_TABSTOP, 145, 30, 75, 66
CONTROL "&kennt", 104, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 95, 40, 40, 14
CONTROL "", 106, "LISTBOX", LBS_STANDARD | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | WS_TABSTOP, 10, 110, 120, 66
CONTROL "", 107, "LISTBOX", LBS_STANDARD | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | WS_TABSTOP, 145, 110, 75, 66
CONTROL "Alle Bekanntschaften:", 111, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE, 10, 100, 72, 8
CONTROL "Text", 108, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE, 145, 100, 83, 8
CONTROL "&Autor", 110, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 180, 10, 40, 14
CONTROL "Robert wird 4mal von dem Fest erzählt.", 109, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE, 95, 65, 40, 33
END
RESOURCE.H
#define ID_DIALOG 100
#define ID_EDIT_NAMEN 101
#define ID_BUTTON_HINZU 102
#define ID_LISTBOX_KENNT_LINKS 103
#define ID_BUTTON_KENNT 104
#define ID_LISTBOX_KENNT_RECHTS 105
#define ID_LISTBOX_ALLE_BEKANNTSCHAFTEN 106
#define ID_LISTBOX_EINZELNE_BEKANNTSCHAFTEN 107
#define ID_TEXT_EINZELNE_BEKANNTSCHAFTEN 108
#define ID_TEXT_ROBERT 109
#define ID_BUTTON_ABOUT 110