Aufgabe 1: Belagerung um Farnsworth Castle
Vorbetrachtungen
Jede Schablone besteht aus m2 Elementen, wobei m die Seitenlänge ist. Insgesamt sind 4 verschiedene Rotationszustände vorhanden, da 360°:90° = 4 gilt. Um einen universellen Einsatz der Schablone für alle nur denkbaren Nachrichten zu erzielen, darf jeder Buchstabe nur einmal durch ein Loch lesbar sein. Pro Rotation werden also m2:4 Buchstaben gelesen. Die Schablone hat somit auch m2:4 Löcher.
Eine Ausnahme von dieser Regel stellen ungerade Seitenlängen m dar. Die Mitte ist entweder ein Loch und immer lesbar oder kein Loch und nie lesbar. Die Anzahl der Löcher der Schablone beträgt also (m2-1):4.
Eine ordnungsgemäße Anordnung an Löchern liest jeden Buchstaben der Vorlage genau einmal. Ein Beispiel für eine einfache Schablone:
Rotiert man ein einzelnes Loch losgelöst von der gesamten Schablone, so stößt jede Drehung dieses Loches auf ein Nicht-Loch.
Man kann also nach folgendem Schema bei der Schablonenerstellung vorgehen:
Bedienung
Das Programm erlaubt die Eingabe einer Nachricht (unverschlüsselter Text) oder eines Ergebnisses (verschlüsselter Text). Diese Namenskonvention zieht sich auch durch die gesamte Dokumentation und den Quellcode.
Je nachdem, welches Eingabefeld zuletzt aktiv war, wird eine Kodierung oder Dekodierung vorgenommen. Rechts neben dem Eingabefeld wird die jeweilige Textlänge angezeigt und ihr Maximalwert, der nicht überschritten werden kann. Wird dieser Wert aber unterschritten, so bringt das Programm eine entsprechende Hinweismeldung. Alle fehlenden Stellen werden mit Leerzeichen aufgefüllt. Beim Ergebnis (oder der Nachricht, wenn dekodiert wurde) fehlende dann alle Leerzeichen hinter dem letzten Buchstaben. Allerdings ist dies mit Problemen verbunden, insbesondere bei der Darstellung der Schablone. Ich rate deshalb zu Texten mit exakt der gleichen Länge wie die Schablone Elemente umfaßt.
Die benutzte Schablone kann durch die Checkbutton-Anordnung beliebig verändert werden. Dabei entspricht ein Häkchen einem Loch. Ob sie den oben beschriebenen Anforderungen entspricht, kann mittels Schablone testen leicht überprüft werden.
Jedoch überprüft das Programm auch vor jeder Kodierung/Dekodierung die Schablone auf Korrektheit. Außerdem aktiviert bzw. deaktiviert das Programm selbständig Checkbuttons, so daß wenn nur noch markierte bzw. gesperrte Buttons vorhanden sind, die Schablone vollständig ist. Das Programm nimmt somit selbständig den 2.Schritt der Schablonenerstellung vor. Das Ergebnis der Ver-/Entschlüsselung wird direkt in das entsprechende Eingabefeld eingetragen, d.h. bei der Kodierung in das Ergebnisfeld.
Die Kodierung erzeigt am Schluß in einem Infofenster das Ergebnis in Schablonenform. Aufgrund der Verwendung proprotionaler Schriften von Windows stehen die Buchstaben nicht immer exakt in Spalten.
Beim Programmstart ist die Schablone aus der Aufgabe standardmäßig gesetzt und der Beispieltext Komm und bring Julee nach Farnsworth im Nachrichten-Eingabefeld eingetragen. Mittels Tastendruck auf Enter wird automatisch der Kodierungs-/Dekodierungsbutton aufgerufen. Das Programm verlangt keine Großschreibung, jedoch wird auch keine Umwandlung vorgenommen. Kleinbuchstaben sind im Ergebnis auch Kleinbuchstaben.
Der Einfachheit halber wird der verschlüsselte Text nicht in einer quadratischen Form, wie die Schablone, sondern direkt hintereinander in das Ergebniseingabefeld ausgegeben. In einem kleinen Infofenster wird bei der Verschlüsselung auch die quadratische Form angezeigt.
Weiterhin läßt sich interaktiv die Seitenlänge der Schablone einstellen. Dazu dient ein Schieberegler. Aus Platzgründen wurde ein Maximum von 6 gewählt. Ebenso ist eine Seitenlänge von <2 nicht sinnvoll. Leider muß bei jeder Änderung die Schablone und die Nachricht/das Ergebnis gelöscht werden.
In der nachfolgenden Abbildung ist die Lösung des Beispielsatzes abzulesen (das führende Leerzeichen ist zu beachten):
Somit läßt sich der Code in folgender quadratischen Form schreiben:
S J U G C
H E T T L
B E R E A A
N L D L U K
R N M N I
T E C H T
Programmdokumentation
Bei der Programmdokumentation beschränkte ich mich auf die Kernmethoden, die sich direkt um die Kodierung/Dekodierung kümmern. Dazu zählen:
BOOL CheckKey();
void Kodieren();
void Dekodieren();
void GetText(TFeld Key);
void SetText(TFeld Key);
void Rotiere(TFeld Alt, TFeld Neu);
Diese Methoden werde ich jetzt im einzelnen erläutern:
CheckKey
Nachdem die Schablone eingelesen wurde, wird ein spezielles Summenfeld auf 0 gesetzt. Die Idee dabei ist, die Schablone in alle 4 Positionen zu rotieren und aufzuaddieren. Da jede Position von nur einem Loch abgedeckt werden darf, müssen alle Elemente des Summenfeldes am Ende den Wert 1 haben. Liegt eine ungerade Seitenlänge vor, so wird die Mitte auch auf 1 gesetzt, da sie nicht benutzt wird und die Prüfroutine nicht stören darf. Das Ergebnis der Methode wird als boolescher Wert zurückgegeben.
Vor jeder Kodierung/Dekodierung wird CheckKey aufgerufen, um unsinnige Ergebnisse zu vermeiden.
Kodieren
Zuerst wird die Nachricht aus dem entsprechendem Eingabefeld ausgelesen. Um Probleme bei zu geringer Buchstabenzahl zu vermeiden, wird jedes in die Berechnung eingehende Nullzeichen durch ein Leerzeichen ersetzt. Als nächste Voraussetzung für die ordnungsgemäße Funktion wird die aktuelle Schablone gelesen, welche sich sofort einer Überprüfung unterziehen muß. Notfalls erfolgt ein Abbruch.
Nun folgt eine Schleife, die alle 4 Rotationen betrachtet. Dabei wird in jedem Durchlauf mittels SetText durch die Löcher der Schablone die Nachricht geschrieben. Danach wird die Schablone um 90° im Uhrzeigersinn gedreht. Am Ende der Schleife ist das Ergebnis praktisch fertig errechnet.
Gemäß der C-Variablendefinition für Zeichenketten wird ein Nullzeichen an das Ergebnis angehängt. Nun folgt noch die Ausgabe auf dem Bildschirm. Zusätzlich wird das Ergebnis in Schablonenform angezeigt.
Dekodieren
Die Funktionsweise ähnelt sehr stark dem Kodieren.
Das Ergebnis, welches in eine Nachricht umgewandelt werden soll, wird ausgelesen und von Nullen bereinigt. Die Schablone wird ebenfalls ausgelesen und überprüft. In der Schleife steht jetzt aber GetText statt SetText, d.h. jetzt wird durch die Löcher gelesen. Das Ende der Methode gleicht wieder dem Kodieren, nur die Nachricht wird in das Nachrichten-Eingabefeld geschrieben. Lediglich die Anzeige der Nachricht in Schablonenform entfällt.
GetText
Diese Methode wird nur beim Dekodieren benutzt. Sie durchläuft alle Elemente der Schablone und überprüft jeweils, ob ein Loch vorliegt. Wenn ja, dann wird an die Nachricht das durchscheinende Zeichen des Ergebnisses angehängt.
SetText
Wiederum ist ein große Ähnlichkeit zu GetText feststellbar. Alleiniger Aufrufer ist diesmal der Kodierer. Ist ein Loch gefunden, wird in die aktuelle XY-Koordinate des Ergebnisses das aktuelle Zeichen der Nachricht geschrieben.
Rotiere
Jedes Element der Schablone wird durchlaufen. Um eine Rotation um 90° im Uhrzeigersinn (mathematisch negativ) zu erzielen, müssen folgende Gleichungen gelten:
xneu = yalt
yneu = AktuelleBreite - 1 - xalt
Weiterführende Erläuterungen
Es ist einfach zu zeigen, daß sich jede Schablone aus 4 gleichen Teilen zusammensetzen läßt. Eines dieser Teile sind die Löcher, die 3 anderen die undurchsichtige Fläche (n ist eine natürliche Zahl):
m = 2n
m2 = 4 n2
m = 2n+1
m2 = (2n+1)2
m2 = 4n2 + 4n + 1
m2 = 4 (n2+n) + 1
Da jedoch die Mitte nicht anwählbar ist, sinkt die Lochanzahl um 1:
m2 -1 = 4 (n2+n)
Somit sind ist gezeigt worden, daß bei geraden Seitenlängen die Fläche ein Vielfaches von 4 ist, bei ungeraden trifft dasselbe zu, nur muß das Mittelloch frei bleiben.
Mit der Schablonenmethode läßt sich aber leider keine Eindeutigkeit erreichen. Dies äußert sich darin, daß je nachdem, bei welcher Rotationsposition der Schablone man anfängt, ein unterschiedliches Ergebnis entsteht. Gleichermaßen kann man bei der Dekodierung vier verschiedene Nachrichten herausbekommen. Der Unterschied ist aber nicht sehr groß, er äußert sich in einem Versatz der Buchstaben. Der Satz beginnt also irgendwo in der Mitte, geht zu Ende und fängt dann an. Durch Ausprobieren aller möglichen Versätze (um 1m, 2m oder 3m) ist es machbar, schnell den Sinn zu erfassen.
Als Ausweg bieten sich zwei Methoden an, die sich in der gegebenen Schablonenbreite unterscheiden: ungerade oder gerade. Danach beschreibe ich ein drittes Verfahren, welches auf alle Breiten anwendbar ist.
Bei Nachrichten mit vielen sich wiederholenden Zeichen, z.B. einer längeren Ziffernfolge, kann man für eine ganz spezielle Nachricht auch eine ganz speziellen Schablone entwerfen. Dabei macht man sich zunutze, daß ein Zeichen durch mehr als ein Loch während der 4 Rotationen gelesen wird. Damit kann der verschlüsselte Code kürzer als die Nachricht sein. Der Einsatzbereich für solche Verfahren ist aber sehr eingeschränkt, da die universelle Einsetzbarkeit verloren geht.
Es besteht auch die Möglichkeit, daß die Nachricht mehr Zeichen als der Schlüssel enthält und sich der eben beschriebene Trick nicht anwenden läßt. Dann muß man die Nachricht in mehrere Blöcke aufteilen, die getrennt verschlüsselt werden. Somit müssen mehrere Schablone verschickt werden. Besonders wichtig ist es, die richtige Reihenfolge zu bestimmen (siehe oben). Dies gilt sowohl für Startrotationsposition jeder Schablone als auch für die Reihenfolge der verschiedene Schablone. Diesen Weg sollte man aber nur gehen, wenn der Zugriff auf große Schablonen nicht möglich ist.
Beispiele
Oben wurde bereits das geforderte Beispiel mit Lösung erwähnt. Deshalb beschränke ich mich jetzt auf eigene:
Standardschlüssel (aus dem Beispiel):
Nachricht: ES GIBT AUCH NACHRICHTEN OHNE SINN !
Schlüssel mit Breite 2, enthält 1 Loch oben links:
Nachricht: ERBE
Ergebnis: ER
EB
Quelltext
EINS.H
#include "resource.h" // ID-Definitionen einbinden
class CEinsApp : public CWinApp // Fensterklasse
{
public:
BOOL InitInstance(); // Start für Dialogerzeugung modifizieren
};
#define MaxBreite 6 // Breite der Schablone
#define MaxInhalt MaxBreite*MaxBreite // Länge der Texte plus abschließendes Zero-Byte
#define Drehungen 4 // 360/90 Drehungen
#define GetSchabloneID(x,y) (ID_SCHABLONE+(x)+(y)*MaxBreite)
// Makro zur Ermittlung der ID eines Schablonenelementes
class CEinsDlg : public CDialog
{
typedef char TFeld[MaxBreite][MaxBreite];
// benötigte Typen definieren
typedef char TText[MaxInhalt+1];
public:
CEinsDlg(CWnd* pParent = NULL); // Konstruktor
enum { IDD = IDD_HAUPTDIALOG }; // DialogID festhalten
protected:
virtual BOOL OnInitDialog(); // Start modifizieren
afx_msg void OnClose(); // Dialog schließen
afx_msg void OnOK(); // Enter-Taste abfangen
afx_msg void OnNachricht(); // Änderung der Nachricht
afx_msg void OnErgebnis(); // Änderung des Ergebnisses
afx_msg void OnKeyItem(); // Änderung der Schablone
afx_msg void OnHScroll(int nSBCode, int nPos, CScrollBar* pScrollBar);
// auf Änderungen der Schablonenbreite reagieren
afx_msg void OnRechnen(); // Kodierung/Dekodierung einleiten
afx_msg void OnTest(); // Schablone testen
afx_msg void OnAbout(); // Programminfo
private:
void UpdateRechnenButton(); // Button auf Kodierung bzw. Dekodierung ändern
void UpdateBreite(); // Schablonenbreite anzeigen
void UpdateLength(int nTextID, int nLength);
// Länge der Nachricht/des Ergebnisses anzeigen
void UpdateKey(); // Schablone aktualisieren
void ShowKey(); // Ergebnis in Schablonenform anzeigen
BOOL CheckKey(); // Schablone überprüfen
void GetKey(); // Schablone aus Bildschirmmaske lesen
void CheckKeyItem(int X, int Y); // Schablonenelemen anpassen
void RemoveZeros(TText cText); // Nullen entfernen
void Kodieren(); // eigentliche Kodierung
void Dekodieren(); // eigentliche Dekodierung
void GetText(TFeld Key); // Text aus einer Schablonenposition lesen
void SetText(TFeld Key); // Text durch ein Schablonenposition schreiben
void Rotiere(TFeld Alt, TFeld Neu); // Schablone rotieren
int m_EditFocus; // aktuelles Edit-Fenster
TText m_Nachricht; // Nachricht
TText m_Ergebnis; // Ergebnis
int m_NachrichtZeichen; // Länge der Nachricht in Zeichen
int m_ErgebnisZeichen; // Länge des Ergebnisses in Zeichen
int m_Zeichen; // Laufvariable beim Kodieren/Dekodieren
TFeld m_Key; // Schablone
int m_Breite; // tatsächliche Schablonenbreite
int m_Inhalt; // Anzahl der Schablonenelemente
DECLARE_MESSAGE_MAP() // MessageHandler installieren
};
EINS.CPP
#include <afxwin.h> // MFC-Bibliothek einbinden
#include "eins.h" // Headerfile einbinden (Definitionen)
CEinsApp Eins; // eigentliches Programm
BOOL CEinsApp::InitInstance() // Start modifizieren
{
Enable3dControls(); // 3D-Stil
CEinsDlg MyDialog; // Dialog statisch erzeugen
m_pMainWnd = &MyDialog; // als Hauptfenster festlegen
return MyDialog.DoModal(); // modal ausführen
}
BEGIN_MESSAGE_MAP(CEinsDlg, CDialog) // alle Messages an Funktionen weiterleiten
ON_EN_SETFOCUS(ID_EDIT_NACHRICHT, OnNachricht)
ON_EN_SETFOCUS(ID_EDIT_ERGEBNIS, OnErgebnis)
ON_EN_CHANGE(ID_EDIT_NACHRICHT, OnNachricht)
ON_EN_CHANGE(ID_EDIT_ERGEBNIS, OnErgebnis)
ON_BN_CLICKED(ID_BUTTON_RECHNEN, OnRechnen)
ON_BN_CLICKED(ID_BUTTON_TEST, OnTest)
ON_BN_CLICKED(ID_BUTTON_ABOUT, OnAbout)
ON_BN_CLICKED(ID_SCHABLONE+00, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+01, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+02, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+03, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+04, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+05, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+06, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+07, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+08, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+09, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+10, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+11, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+12, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+13, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+14, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+15, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+16, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+17, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+18, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+19, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+20, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+21, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+22, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+23, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+24, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+25, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+26, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+27, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+28, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+29, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+30, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+31, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+32, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+33, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+34, OnKeyItem)
ON_BN_CLICKED(ID_SCHABLONE+35, OnKeyItem)
ON_WM_HSCROLL()
ON_WM_CLOSE()
END_MESSAGE_MAP()
CEinsDlg::CEinsDlg(CWnd* pParent):CDialog(IDD, pParent)
{
m_EditFocus = ID_EDIT_NACHRICHT; // Nachricht als Standard-Edit festlegen
m_Breite = 6; // Breite 6 vorgeben
m_Inhalt = m_Breite*m_Breite; // Elemente aus Breite errechnen
}
BOOL CEinsDlg::OnInitDialog() // Start modifizieren
{
CDialog::OnInitDialog(); // vererbte Methode aufrufen
((CScrollBar*)GetDlgItem(ID_SCROLLBAR_BREITE))->SetScrollRange(2,6,TRUE);
// Scrollbar auf Bereich 2..6 einstellen
UpdateRechnenButton(); // auf Kodierung als Vorgabe schalten
UpdateBreite(); // Breite 6 anzeigen
CheckDlgButton(GetSchabloneID(2,0),1); // Schablone aus Aufgabe voreinstellen
CheckDlgButton(GetSchabloneID(3,0),1);
CheckDlgButton(GetSchabloneID(4,1),1);
CheckDlgButton(GetSchabloneID(1,2),1);
CheckDlgButton(GetSchabloneID(3,2),1);
CheckDlgButton(GetSchabloneID(0,4),1);
CheckDlgButton(GetSchabloneID(3,4),1);
CheckDlgButton(GetSchabloneID(5,4),1);
CheckDlgButton(GetSchabloneID(0,5),1);
UpdateKey(); // Schablone aktualisieren
CEdit *pEdit2 = (CEdit*)GetDlgItem(ID_EDIT_ERGEBNIS);
CEdit *pEdit1 = (CEdit*)GetDlgItem(ID_EDIT_NACHRICHT);
pEdit2->LimitText(m_Inhalt); // Maximallänge der Texte
pEdit1->LimitText(m_Inhalt);
pEdit2->SetFocus(); // einmal aktivieren, um auf den Focus reagierende
pEdit1->SetFocus(); // Methoden aufzurufen
return FALSE; // Focus nicht verändern
}
void CEinsDlg::OnClose() // Dialog schließen
{
if (MessageBox("Wirklich beenden ?","Ende",MB_YESNO|MB_ICONQUESTION)==IDYES)
CDialog::OnClose(); // Sicherheitsabfrage
}
void CEinsDlg::OnOK() // Enter-Taste abfangen
{
OnRechnen(); // Kodierung/Dekodierung durchführen
}
void CEinsDlg::OnNachricht() // Änderung der Nachricht
{
CEdit *pEdit = (CEdit*)GetDlgItem(ID_EDIT_NACHRICHT);
m_NachrichtZeichen = pEdit->LineLength(); // Länge ausgeben
m_EditFocus = ID_EDIT_NACHRICHT; // EditFocus speichern
UpdateLength(ID_TEXT_NACHRICHT, m_NachrichtZeichen);
UpdateRechnenButton(); // Rechnenbutton anpassen
}
void CEinsDlg::OnErgebnis() // Änderung des Ergebnisses
{
CEdit *pEdit = (CEdit*)GetDlgItem(ID_EDIT_ERGEBNIS);
m_ErgebnisZeichen = pEdit->LineLength(); // Länge ausgeben
m_EditFocus = ID_EDIT_ERGEBNIS; // EditFocus speichern
UpdateLength(ID_TEXT_ERGEBNIS, m_ErgebnisZeichen);
UpdateRechnenButton(); // Rechnenbutton anpassen
}
void CEinsDlg::OnKeyItem() // Änderung der Schablone
{
int KeyID = GetCurrentMessage()->wParam - ID_SCHABLONE;
// BasisID abziehen
if ((KeyID>=0)&&(KeyID<MaxInhalt)) // wirklich Schablone ?
{
int X = KeyID%MaxBreite; // Koordinaten errechnen
int Y = KeyID/MaxBreite;
CheckKeyItem(X,Y); // CheckButton invertieren
}
}
void CEinsDlg::OnRechnen() // Kodierung/Dekodierung einleiten
{
switch(m_EditFocus) // Unterscheidung
{
case ID_EDIT_NACHRICHT:
if ((m_NachrichtZeichen==m_Inhalt)||
(MessageBox("Die Nachricht enthält weniger oder mehr Zeichen als der Schlüssel !\nTrotzdem fortfahren ?",
"Problem",MB_YESNO|MB_ICONQUESTION)==IDYES))
Kodieren(); // bei zu wenig Zeichen Sicherheitsabfrage
break;
case ID_EDIT_ERGEBNIS:
if ((m_ErgebnisZeichen==m_Inhalt)||
(MessageBox("Das Ergebnis enthält weniger oder mehr Zeichen als der Schlüssel !\nTrotzdem fortfahren ?",
"Problem",MB_YESNO|MB_ICONQUESTION)==IDYES))
Dekodieren(); // bei zu wenig Zeichen Sicherheitsabfrage
break;
}
}
void CEinsDlg::OnTest() // Schablone testen
{
GetKey(); // Schablone aus Dialog lesen
if (CheckKey()) // überprüfen (ungültig wird dort abgefangen)
MessageBox("Die Schablone ist gültig.","Schablone testen",MB_OK|MB_ICONINFORMATION);
// bei gültig Meldung
}
void CEinsDlg::OnAbout() // Programminfo
{
MessageBox("geschrieben von Stephan Brumme\nin Watcom C++ 10.6 mit MFC 3.2","Über...",MB_OK|MB_ICONINFORMATION);
}
void CEinsDlg::OnHScroll(int nSBCode, int nPos, CScrollBar* pScrollBar)
// auf Änderungen der Schablonenbreite reagieren
{
switch (nSBCode) // je nach Aktion
{
case SB_LINEDOWN: // rechts
case SB_PAGEDOWN:
if ((m_Breite>=2)&&(m_Breite<MaxBreite-2))
m_Breite += 2; // wenn im gültigen Bereich, dann erhöhen
break;
case SB_LINEUP: // links
case SB_PAGEUP:
if ((m_Breite>2)&&(m_Breite<=MaxBreite))
m_Breite -= 2; // wenn im gültigen Bereich, dann erniedrigen
break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
m_Breite = nPos/2*2; // direkt Position bestimmen
break;
}
m_Inhalt = m_Breite*m_Breite; // neue Elementanzahl
SetDlgItemText(ID_EDIT_NACHRICHT, "");
SetDlgItemText(ID_EDIT_ERGEBNIS, "");
UpdateBreite(); // Bildschirm anpassen
UpdateKey();
}
void CEinsDlg::UpdateRechnenButton() // Button auf Kodierung bzw. Dekodierung ändern
{
char cNachricht[] = "&Verschlüsseln"; // beide Buttonbeschriftungen
char cErgebnis[] = "&Entschlüsseln";
switch(m_EditFocus) // je nach Editfocus Beschriftung setzen
{
case ID_EDIT_NACHRICHT:
SetDlgItemText(ID_BUTTON_RECHNEN, cNachricht);
break;
case ID_EDIT_ERGEBNIS:
SetDlgItemText(ID_BUTTON_RECHNEN, cErgebnis);
break;
}
}
void CEinsDlg::UpdateBreite() // Schablonenbreite anzeigen
{
char cBreite[2]; // Zwischenpuffer
sprintf(cBreite, "%i", m_Breite); // Schablonenbreite dort eintragen
SetDlgItemText(ID_TEXT_BREITE, cBreite); // und anzeigen
((CScrollBar*)GetDlgItem(ID_SCROLLBAR_BREITE))->SetScrollPos(m_Breite, TRUE);
// ScrollBar anpassen
((CEdit*)GetDlgItem(ID_EDIT_ERGEBNIS))->LimitText(m_Inhalt);
((CEdit*)GetDlgItem(ID_EDIT_NACHRICHT))->LimitText(m_Inhalt);
for (int Y=0; Y<MaxBreite; Y++) // jede Position durchlaufen
for (int X=0; X<MaxBreite; X++)
{
GetDlgItem(GetSchabloneID(X,Y))->EnableWindow((X<m_Breite)&&(Y<m_Breite));
// nur Checkbuttons innerhalb der aktuellen
// Schablonengröße anklickbar
CheckDlgButton(GetSchabloneID(X,Y),FALSE);
// bisherige Schablone löschen
}
OnNachricht(); // neue Breite mitteilen
OnErgebnis();
}
void CEinsDlg::UpdateLength(int nTextID, int nLength)
// Länge der Nachricht/des Ergebnisses anzeigen
{
char m_LengthBuf[20]; // Ausgabe der Länge von Nachricht bzw. Ergebnis
sprintf(m_LengthBuf, "Zeichen: %i/%i", nLength, m_Inhalt);
SetDlgItemText(nTextID, m_LengthBuf);
// Länge ausgeben
}
void CEinsDlg::UpdateKey()
{
for (int Y=0; Y<m_Breite; Y++) // jede Position durchlaufen
for (int X=0; X<m_Breite; X++)
if (IsDlgButtonChecked(GetSchabloneID(X,Y))==1)
CheckKeyItem(X,Y);
}
void CEinsDlg::ShowKey() // Ergebnis in Schablonenform anzeigen
{
char cAnzeige[200] = ""; // zur Ausgabe
for (int Lauf=0; Lauf<m_Inhalt; Lauf++)
{ // komplettes Ergebnis durchlaufen
if (m_Ergebnis[Lauf]==' ') // Leerzeichen ?
sprintf(cAnzeige, "%s_ ", cAnzeige);
else // durch Unterstrich symbolisieren
sprintf(cAnzeige, "%s%c ", cAnzeige, m_Ergebnis[Lauf]);
if ((Lauf%m_Breite)==m_Breite-1) // Zeilenumbruch
sprintf(cAnzeige, "%s\n", cAnzeige);
}
MessageBox(cAnzeige, "Ergebnis in Schablonenform", MB_OK|MB_ICONINFORMATION);
// ausgeben
}
BOOL CEinsDlg::CheckKey() // Schablone überprüfen
{
TFeld Summe, Neu, Dreh;
GetKey(); // Schablone holen und überprüfen
memset(Summe, 0, sizeof(Summe)); // Summenfeld leeren
memcpy(Neu, m_Key, sizeof(Neu)); // Originalschablone bleibt unverändert
for (int Lauf=0; Lauf<Drehungen; Lauf++) // alle 4 Drehungen vollziehen
{
for (int Y=0; Y<m_Breite; Y++) // jede Position durchlaufen
for (int X=0; X<m_Breite; X++)
Summe[Y][X]+=Neu[Y][X]; // Schablone aufaddieren
Rotiere(Neu, Dreh); // drehen
memcpy(Neu, Dreh, sizeof(Neu)); // neue Schablone speichern
}
for (int Y=0; Y<m_Breite; Y++) // jede Position durchlaufen
for (int X=0; X<m_Breite; X++)
if (Summe[Y][X]!=1) // falls Problem mit Lochanzahl
{
MessageBox("Ungültige Schablone !", "Problem",MB_OK|MB_ICONSTOP);
return FALSE; // Fehlermeldung, Rückmeldung an Aufrufer
}
return TRUE; // alles ok
}
void CEinsDlg::GetKey() // Schablone aus Bildschirmmaske lesen
{
for (int Y=0; Y<m_Breite; Y++) // jede Position durchlaufen
for (int X=0; X<m_Breite; X++)
m_Key[Y][X]=IsDlgButtonChecked(GetSchabloneID(X,Y));
// Zustand auslesen
}
void CEinsDlg::CheckKeyItem(int X, int Y) // Schablonenelemente anpassen
{
int Check = IsDlgButtonChecked(GetSchabloneID(X,Y));
// Zustand ermitteln
for (int Lauf=0; Lauf<3; Lauf++) // durch Rotation verknüpfte Elemente
{
int TempY = m_Breite-1-X; // rotieren
X = Y;
Y = TempY;
GetDlgItem(GetSchabloneID(X,Y))->EnableWindow(1-Check);
// Aktivierbarkeit einstellen
}
}
void CEinsDlg::RemoveZeros(TText cText) // Nullen entfernen
{
BOOL bEnde = FALSE;
for (int Lauf=0; Lauf<m_Inhalt; Lauf++) // jedes Zeichen untersuchen
if (bEnde)
cText[Lauf] = ' '; // durch Leerzeichen ersetzen
else
if (!cText[Lauf])
bEnde = TRUE;
}
void CEinsDlg::Kodieren() // eigentliche Kodierung
{
TFeld Neu, Dreh;
GetDlgItemText(ID_EDIT_NACHRICHT, m_Nachricht, m_Inhalt+1);
RemoveZeros(m_Nachricht);
m_Zeichen = 0; // Nachricht holen, bei Zeichen 0 anfangen
GetKey(); // Schablone holen und überprüfen
if (!CheckKey())
return;
memcpy(Neu, m_Key, sizeof(Neu)); // Originalschablone bleibt unverändert
for (int Lauf=0; Lauf<Drehungen; Lauf++) // alle 4 Drehungen vollziehen
{
SetText(Neu); // durch Löcher der Schablone schreiben
Rotiere(Neu, Dreh); // drehen
memcpy(Neu, Dreh, sizeof(Neu)); // neue Schablone speichern
}
m_Ergebnis[m_Zeichen] = 0; // abschließendes Zero-Byte
SetDlgItemText(ID_EDIT_ERGEBNIS, m_Ergebnis);
// Ergebnis ausgeben
ShowKey(); // Ergebnis in Schablonenform anzeigen
}
void CEinsDlg::Dekodieren() // eigentliche Dekodierung
{
TFeld Neu, Dreh;
GetDlgItemText(ID_EDIT_ERGEBNIS, m_Ergebnis, m_Inhalt+1);
RemoveZeros(m_Ergebnis);
m_Zeichen = 0; // Ergebnis holen, bei Zeichen 0 anfangen
GetKey(); // Schablone holen und überprüfen
if (!CheckKey())
return;
memcpy(Neu, m_Key, sizeof(Neu)); // Originalschablone bleibt unverändert
for (int Lauf=0; Lauf<Drehungen; Lauf++) // alle 4 Drehungen vollziehen
{
GetText(Neu); // aus allen Löchern der Schablone lesen
Rotiere(Neu, Dreh); // drehen
memcpy(Neu, Dreh, sizeof(Neu)); // neue Schablone speichern
}
m_Nachricht[m_Zeichen] = 0; // abschließendes Zero-Byte
SetDlgItemText(ID_EDIT_NACHRICHT, m_Nachricht);
// Nachricht ausgeben
}
void CEinsDlg::GetText(TFeld Key) // Text aus einer Schablonenposition lesen
{
for (int Y=0; Y<m_Breite; Y++) // jede Position durchlaufen
for (int X=0; X<m_Breite; X++)
if (Key[Y][X]) // Loch ?
m_Nachricht[m_Zeichen++]=m_Ergebnis[Y*MaxBreite+X];
// ja, Zeichen lesen
}
void CEinsDlg::SetText(TFeld Key) // Text durch ein Schablonenposition schreiben
{
for (int Y=0; Y<m_Breite; Y++) // jede Position durchlaufen
for (int X=0; X<m_Breite; X++)
if (Key[Y][X]) // Loch ?
m_Ergebnis[Y*m_Breite+X]=m_Nachricht[m_Zeichen++];
// ja, Zeichen schreiben
}
void CEinsDlg::Rotiere(TFeld Alt, TFeld Neu) // Schablone rotieren
{
for (int Y=0; Y<m_Breite; Y++) // jede Position durchlaufen
for (int X=0; X<m_Breite; X++)
Neu[Y][X] = Alt[m_Breite-1-X][Y]; // um 90 Grad im Uhrzeigersinn drehen
}
EINS.DLG
1000 DIALOG FIXED IMPURE 22, 34, 301, 170
STYLE DS_MODALFRAME | DS_3DLOOK | DS_CENTER | DS_CENTERMOUSE | WS_OVERLAPPED | WS_CAPTION | WS_VISIBLE | WS_SYSMENU
CAPTION "Aufgabe 1"
FONT 8, "Helv"
BEGIN
CONTROL "KOMM UND BRING JULEE NACH FARNSWORTH", 1001, "EDIT", ES_LEFT | ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 41, 3, 169, 12
CONTROL "Nachricht:", 101, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE, 3, 3, 36, 8
CONTROL "Ergebnis:", 102, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE, 3, 20, 37, 8
CONTROL "", 1002, "EDIT", ES_LEFT | ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 41, 20, 169, 12
CONTROL "Push", 1003, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 220, 40, 75, 14
CONTROL "&Autor", 1005, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 220, 80, 75, 14
CONTROL "Edit", 103, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE, 220, 3, 75, 8
CONTROL "Edit", 104, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE, 220, 20, 75, 8
CONTROL "", 2000, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 50, 40, 11, 10
CONTROL "", 2001, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 70, 40, 13, 10
CONTROL "", 2002, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 90, 40, 15, 10
CONTROL "Schablone:", 105, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE, 3, 40, 44, 8
CONTROL "", 2003, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 110, 40, 11, 10
CONTROL "", 2004, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 130, 40, 15, 10
CONTROL "", 2005, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 150, 40, 15, 10
CONTROL "", 2006, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 50, 60, 13, 10
CONTROL "", 2007, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 70, 60, 13, 10
CONTROL "", 2008, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 90, 60, 12, 10
CONTROL "", 2009, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 110, 60, 13, 10
CONTROL "", 2010, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 130, 60, 12, 10
CONTROL "", 2011, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 150, 60, 16, 10
CONTROL "", 2012, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 50, 80, 13, 10
CONTROL "", 2018, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 50, 100, 13, 10
CONTROL "", 2024, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 50, 120, 12, 10
CONTROL "", 2030, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 50, 140, 15, 10
CONTROL "", 2013, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 70, 80, 11, 10
CONTROL "", 2014, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 90, 80, 12, 10
CONTROL "", 2015, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 110, 80, 13, 10
CONTROL "", 2016, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 130, 80, 11, 10
CONTROL "", 2017, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 150, 80, 16, 10
CONTROL "", 2019, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 70, 100, 11, 10
CONTROL "", 2020, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 90, 100, 12, 10
CONTROL "", 2021, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 110, 100, 15, 10
CONTROL "", 2022, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 130, 100, 12, 10
CONTROL "", 2023, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 150, 100, 13, 10
CONTROL "", 2025, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 70, 120, 13, 10
CONTROL "", 2026, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 90, 120, 11, 10
CONTROL "", 2027, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 110, 120, 12, 10
CONTROL "", 2028, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 130, 120, 11, 10
CONTROL "", 2029, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 150, 120, 12, 10
CONTROL "", 2031, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 70, 140, 11, 10
CONTROL "", 2033, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 110, 140, 12, 10
CONTROL "", 2032, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 90, 140, 13, 10
CONTROL "", 2034, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 130, 140, 12, 10
CONTROL "", 2035, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 150, 140, 12, 10
CONTROL "&Schablone testen", 1004, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 220, 60, 75, 14
CONTROL "Breite:", 106, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE, 180, 154, 23, 8
CONTROL "", 1006, "SCROLLBAR", SBS_HORZ | WS_CHILD | WS_VISIBLE, 58, 154, 113, 10
CONTROL "6", 107, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE, 204, 154, 9, 8
END
RESOURCE.H
/* Resourcen für Aufgabe 1 des BWINF 97/98
Autor: Stephan Brumme
*/
#define IDD_HAUPTDIALOG 1000 // ID des Dialogfensters
#define ID_EDIT_NACHRICHT 1001 // Dialogelemente
#define ID_EDIT_ERGEBNIS 1002
#define ID_BUTTON_RECHNEN 1003
#define ID_BUTTON_TEST 1004
#define ID_BUTTON_ABOUT 1005
#define ID_SCROLLBAR_BREITE 1006
#define ID_TEXT_NACHRICHT 103
#define ID_TEXT_ERGEBNIS 104
#define ID_TEXT_BREITE 107
#define ID_SCHABLONE 2000 // BasisID der Schablonen-RadioButtons
// gesamter Bereich 2000-2035