Hallo werte Community,
einige von euch kennen vielleicht schon dieses Ratespiel, das seit einiger Zeit (nicht nur?) auf Smartphones sein Unwesen treibt: "4 Bilder 1 Wort". Es beinhaltet im Grunde (mittlerweile) über 1400 Rätsel, die aus jeweils auf 4 Bildern, 12 Buchstaben und einem Eingabefeld für die Lösung bestehen. Die 4 Bilder haben alle eine Gemeinsamkeit - eine gemeinsame Bedeutung, Bezeichnung, Tätigkeit. Zumindest wird das behauptet (ich stimme mit den Erstellern der einzelnen Rätsel nicht immer überein). Und nach dem Wort für diese Gemeinsamkeit wird gesucht.
Beispiel: Auf den vier Bildern sieht man einen roten Planeten, einen Schokoriegel, ein Sternzeichensymbol und einen griechische Statue eines Kriegers. Die 12 Buchstaben sind scheinbar vollkommen zufällig, aber irgendwo sind auch die Buchstaben M, A, R und S mit dabei - natürlich nicht unbedingt in der Reihenfolge. Das einzige was man noch weiß: Das gesuchte Wort hat vier Buchstaben.
Da die Macher der Bilderrätsel manchmal etwas sehr abstruse Vorstellungen davon haben, was auf manchen Bildern zu sehen sein soll und wie das mit den anderen zusammenpasst, und ob man in einem Spiel, das den deutschen Sprachschatz nutzt, so Begriffe wie "Facebook", "Hiphop" oder "Pasta" vorkommen können, darüber lässt sich sicher auch gut streiten, ist es manchmal wirklich knifflig auf den richtigen Begriff zu kommen. Zum Glück gibt es bereits im Internet mehrere Seiten, die mögliche Lösungen präsentieren (einige wenige werden sogar recht zügig auf den aktuellen Stand gebracht, wenn es mal wieder ein Rätsel-Update gab).
Das Finden der richtigen Lösung (bzw. des richtigen Rätsels, um die Lösung abzugreifen) ist dabei mit ein paar Hürden verbunden: Zum einen müssen die Bilder mit Worten beschrieben werden, da sie vermutlich aus Copyright-Gründen nicht einfach so auf einer x-beliebigen Webseite verwendet werden dürfen, und wenn der Lösungsanbieter meint, da ist eine Apfelsine zu sehen und man selbst sucht aber nach Orange, dann findet man halt im ersten Moment nichts. Eine andere Hürde ist, dass die Rätsel bei jedem Spieler in einer anderen (zufälligen) Reihenfolge erscheinen. Die Nummer der Aufgabe bzw. die Anzahl der bisher gelösten Aufgaben helfen also nicht wirklich dabei, die passende Zeile mit der gesuchten Lösung zu finden.
Langer Rede kurzer Sinn: Ich habe mir zu diesem Zweck ein kleines Programm geschrieben - zunächst in C# - und dachte mir, das wäre ein prima - weil auch übersichtliches - Projekt um es mit euch zu teilen, es zu diesem Zweck in VB neu entstehen und euch am Entwicklungsprozess teilhaben zu lassen.
Grundkonzept
Ziel des Programms soll es sein, dass man ohne Beschreibung der Bilder auskommt und dabei stattdessen all die anderen Informationen mit ins Boot holt, die so ein Rätsel bereitstellt: also Anzahl der Buchstaben des Lösungsworts und die 12 bereitgestellten Buchstaben zum Lösen (von denen einige unbrauchbar sind). Es ist außerdem bekannt, dass es kein Wort mit weniger als 3 (da bin ich allerdings nicht sicher, bisher gibts noch kein zweibuchstabiges Wort) oder mit mehr als 8 Buchstaben gibt (mehr als 8 Buchstaben gibt das Layout des Spiels nicht her).
Ich möchte also mit Hilfe der 12 verfügbaren Buchstaben alle die möglichen Lösungswörter finden, die sich mit diesem Satz bilden lassen. Als zusätzliche Einschränkung sollen hiervon wiederum nur diejenigen gewählt werden, bei denen die Anzahl der Buchstaben des Lösungsworts übereinstimmt. Die möglichen Lösungswörter sollen als eine Liste von Wörtern zur Verfügung stehen, damit wir nicht irgendwelche Lexika wälzen oder referenzieren müssen und dabei so viele mögliche oder gar unsinnige Lösungsvorschläge entstehen können, die das Spiel selbst sowieso (noch) nicht beinhaltet.
Auch hierbei kann es noch passieren, dass zu einem Buchstabensatz mehrere Lösungswörter passen, aber die Menge ist dann auf jeden Fall auf einen Blick überschaubar und kann dann mit Hilfe der Bilder vom Spieler leicht weiter eingeschränkt werden. In ganz harten Fällen, wo selbst der Bildinhalt eine der mehrfach vorgegebenen Lösungsmöglichkeiten nicht preisgibt, muss halt das altbewährte Try-and-Error herhalten. Mit dem eingeschränkten (zur Auswahl passenden) Satz an Vorschlägen ist das aber durchaus weniger aufwändig als sämtliche Wörter auszuprobieren oder gar alle 12 Buchstaben zu permutieren.
In den Weiten des Internets habe ich diese Seite gefunden, die auf einer HTML-Seite eine komplette Tabelle mit allen möglichen Lösungen enthält: touchportal.de/4bilder/4-bilder-1-wort-komplettlosung/
Start
Irgendwo müssen wir mit dem Projekt ja beginnen, also starten wir die Entwicklungsumgebung und legen ein neues Windows Forms Projekt an. Um die Form kümmer ich mich aber erst später. Jetzt gehts erstmal um die Datenbasis.
Wortliste
Um also Wörter finden zu können, brauchen wir eine Liste aller möglichen Lösungen. Auf den ersten Blick bietet sich der Typ List(Of String) an. Mir schwebte aber eher eine Liste vor, die direkt auch eine Methode zum Finden der Wörter bereithält. Und das was wir an Informationen für die Suche haben, hilft der List(Of T) leider nicht bei der Suche. Aus den drei offensichtlichen Ansätzen
Die Funktion
Wortliste füllen
Bevor wir überhaupt irgendein Ergebnis bekommen, muss natürlich eine Wortliste zur Verfügung stehen, die mit den passenden Lösungen gefüllt ist. Da die Wörter alle auf einer Webseite zur Verfügung stehen, die zudem (zumindest zurzeit) bei Rätsel-Updates relativ zeitnah aktualisiert wird, bietet es sich an, diese jeweils direkt von dort zu beziehen.
Da ich mir die Scherereien mit schlecht programmiertem HTML oder XML schenken will, benutze ich keine Klasse zum Interpretieren solcher Dokumente, sondern wusel mich auf String-Ebene durch den Quelltext. Die fragliche Seite stellt die Lösungen in einer HTML-Tabelle dar, wobei alle Lösungswörter selbst in Tabellenzellen (
Als zusätzliche Schwierigkeit kommt hinzu, dass hier manchmal zwei alternative Lösungen in einer Zelle stehen - vermutlich weil der Anbieter der Lösung nicht mehr genau wusste, ob nun z.B. "Disaster" oder "Desaster" die richtige Lösung war. Zum Glück folgt er dabei (bisher) immer demselben Schema, dass zwei Wörter durch "/" voneinander getrennt sind. Wir nehmen der Einfachheit halber beide mit auf und überlassen es dem zur Verfügung stehenden Buchstabensatz bzw. der Intelligenz des Benutzers, zu entscheiden, was nun tatsächlich die richtige Lösung ist.
Desweiteren gibt es - aus einem mir unerfindlichen Grund - in der Tabelle einige Lösungen, die mit "Level: xxx" oder "Level xxx" angezeigt werden. Das möchte ich nach Möglichkeit ebenfalls filtern.
Als Ergebnis kam eine Funktion heraus, die die Informationen aus dem Web lädt, die Wörter wie beschrieben herausfiltert und als Wortliste zur Verfügung stellt.
Da sie direkt mit der Klasse
Damit sind alle Voraussetzungen erfüllt und wir können uns an die Form machen.
MainForm
So einfach wie möglich: Eine TextBox als Eingabefeld. Wir setzen - finde ich persönlich gefälliger - die Eigenschaft
Für die Auswahl, wieviele Buchstaben das Lösungswort haben sollte, habe ich eine Reihe aus 8 RadioButtons vorgesehen, jeweils beschriftet mit "Alle" und den Zahlen "2" bis "8". Ich hasse es, sowas von vornherein auf die Form zu klatschen, also platzier ich nur einen Button und lasse die restlichen 7 dynamisch generieren, sozusagen als Kopie des vorgegebenen, wobei der vorplatzierte praktisch die Position vorgibt und alle anderen sich dahinter reihen sollen.
Zu guter Letzt wollen wir auch die Ergebnisse irgendwo sehen, also setzen wir eine beliebige Listbox irgendwo hin. Die muss ja nur anzeigen. Mehr nicht.
Im Form_Load Event richten wir das alles soweit her, d.h. wir besorgen uns die Wortliste aus dem Web und erzeugen sieben Kopien unseres Buttons. Den Click-Handler weisen wir auch gleich zu, und damit wir uns nicht von Meldungen im Fehlerfenster nerven lassen müssen, legen wir dafür euch gleich den Rumpf mit an:
Buttons per Taste
Bei den ersten Versionen meines Programms hatte mich dann aber doch eine Kleinigkeit gestört. Hatte ich brav meine 12 Buchstaben abgetippt und wollte nun die Auswahl auf Wörter bestimmter Länge begrenzen, musste ich mit der rechten Hand von der Tastatur auf die Maus wechseln um den Button zu klicken. Alternativ hätte ich mehrere TABs und ENTER nehmen können, aber das ist doch viel zu viel Tastengefrickel. Auch Shortcut-Keys, die ich dann mit Alt+irgendwas erreiche war mir zu viel Verrenkung, immerhin gebe ich ins Textfeld auschließlich Buchstaben ein. Wieso also nicht die Zahlen für den Direktzugriff auf die Buttons missbrauchen?
So kam es, dass im vorigen Code-Abschnitt sich die Zeile "Me.KeyPreview = True" eingeschlichen hat.
Um die Tasten dann auch entsprechend abzufangen, überschreibe ich einfach die OnKeyPress-Methode der Form-Klasse. Dort werden nicht nur die Tasten "0" und "2" bis "8" auf die entsprechenden Button-Clicks umgelenkt, sondern ich fange zudem die Escape-Taste ab, um das Programm mit Hilfe eines Tastendrucks beenden zu können. Ich liebe solche Shortcuts
Natürlich könnte man hier aus der gedrückten Taste gleich die Wortlänge ermitteln und die Listbox entsprechend füllen, aber es ging mir hierbei darum, exakt das Auslösen des Buttons mit der entsprechenden Zifferntaste zu bewirken. Wollte ich irgendwann das Verhalten eines Buttons ändern, müsste ich dann Code an zwei unterschiedlichen Stellen ändern. Das halte ich für ineffizient. Darum der Umweg über den Button und
Lösungen suchen (und finden)
Das wichtigste fehlt ja eigentlich noch: Nämlich die Verknüpfung der eingegebenen Buchstaben mit der Suchfunktion. Das erledigen wir einfach im TextChanged-Event der Textbox, so sehen wir ggf. schon während der Eingabe der verfügbaren Buchstaben, was eventuell als Lösung in Betracht kommen könnte. Da wir über die Buttons später steuern wollen, auf wieviel Buchstaben die Lösungswörter beschränkt sein sollen, benutzen wir dafür eine eigene Variable auf Klassenebene.
Restliche Logik
Fehlen noch die eigentlichen Events für Button.Click und TextBox.TextChanged:
Das Integer.TryParse macht aus dem Text "Alle" des ersten Buttons automatisch den Wert 0. Das ist recht praktisch, weil das genau der Wert ist den wir brauchen, um die Lösungsliste nicht auf eine bestimmte Wortlänge einzuschränken.
Was fehlt?
Ein paar Kleinigkeiten kann man sicher noch verbessern. Zum einen das Layout, wie an der entsprechenden Stelle aber schon angemerkt.
Dann ist mir aufgefallen, dass wenn man mit seinen Wurstfingern statt eine der Ziffern 2 bis 8 oder 0 doch mal die 1 oder 9 erwischen sollte, dass diese dann doch wieder an das Textfeld geht. Das kann man aber auch noch abfangen. (Hausaufgabe! )
Dadurch dass bei jedem Programmstart die Webseite abgerufen und analysiert wird zieht der Start sich für so eine Pipifax-Anwendung ziemlich hin. Eine Verbesserung könnte es sein, die Daten aus dem Web nur auf spezielle Anforderung (Button?) zu laden und dann lokal in einer Datei oder Datenbank zu speichern, die stattdessen verwendet wird.
Vielleicht kennt und nutzt ja der eine oder andere von euch die App "4 Bilder 1 Wort" und findet mit der Hilfe dieses Tools endlich die Lösung, an der er seit Wochen festhängt und deshalb nicht weiterspielen kann?
einige von euch kennen vielleicht schon dieses Ratespiel, das seit einiger Zeit (nicht nur?) auf Smartphones sein Unwesen treibt: "4 Bilder 1 Wort". Es beinhaltet im Grunde (mittlerweile) über 1400 Rätsel, die aus jeweils auf 4 Bildern, 12 Buchstaben und einem Eingabefeld für die Lösung bestehen. Die 4 Bilder haben alle eine Gemeinsamkeit - eine gemeinsame Bedeutung, Bezeichnung, Tätigkeit. Zumindest wird das behauptet (ich stimme mit den Erstellern der einzelnen Rätsel nicht immer überein). Und nach dem Wort für diese Gemeinsamkeit wird gesucht.
Beispiel: Auf den vier Bildern sieht man einen roten Planeten, einen Schokoriegel, ein Sternzeichensymbol und einen griechische Statue eines Kriegers. Die 12 Buchstaben sind scheinbar vollkommen zufällig, aber irgendwo sind auch die Buchstaben M, A, R und S mit dabei - natürlich nicht unbedingt in der Reihenfolge. Das einzige was man noch weiß: Das gesuchte Wort hat vier Buchstaben.
Da die Macher der Bilderrätsel manchmal etwas sehr abstruse Vorstellungen davon haben, was auf manchen Bildern zu sehen sein soll und wie das mit den anderen zusammenpasst, und ob man in einem Spiel, das den deutschen Sprachschatz nutzt, so Begriffe wie "Facebook", "Hiphop" oder "Pasta" vorkommen können, darüber lässt sich sicher auch gut streiten, ist es manchmal wirklich knifflig auf den richtigen Begriff zu kommen. Zum Glück gibt es bereits im Internet mehrere Seiten, die mögliche Lösungen präsentieren (einige wenige werden sogar recht zügig auf den aktuellen Stand gebracht, wenn es mal wieder ein Rätsel-Update gab).
Das Finden der richtigen Lösung (bzw. des richtigen Rätsels, um die Lösung abzugreifen) ist dabei mit ein paar Hürden verbunden: Zum einen müssen die Bilder mit Worten beschrieben werden, da sie vermutlich aus Copyright-Gründen nicht einfach so auf einer x-beliebigen Webseite verwendet werden dürfen, und wenn der Lösungsanbieter meint, da ist eine Apfelsine zu sehen und man selbst sucht aber nach Orange, dann findet man halt im ersten Moment nichts. Eine andere Hürde ist, dass die Rätsel bei jedem Spieler in einer anderen (zufälligen) Reihenfolge erscheinen. Die Nummer der Aufgabe bzw. die Anzahl der bisher gelösten Aufgaben helfen also nicht wirklich dabei, die passende Zeile mit der gesuchten Lösung zu finden.
Langer Rede kurzer Sinn: Ich habe mir zu diesem Zweck ein kleines Programm geschrieben - zunächst in C# - und dachte mir, das wäre ein prima - weil auch übersichtliches - Projekt um es mit euch zu teilen, es zu diesem Zweck in VB neu entstehen und euch am Entwicklungsprozess teilhaben zu lassen.
Grundkonzept
Ziel des Programms soll es sein, dass man ohne Beschreibung der Bilder auskommt und dabei stattdessen all die anderen Informationen mit ins Boot holt, die so ein Rätsel bereitstellt: also Anzahl der Buchstaben des Lösungsworts und die 12 bereitgestellten Buchstaben zum Lösen (von denen einige unbrauchbar sind). Es ist außerdem bekannt, dass es kein Wort mit weniger als 3 (da bin ich allerdings nicht sicher, bisher gibts noch kein zweibuchstabiges Wort) oder mit mehr als 8 Buchstaben gibt (mehr als 8 Buchstaben gibt das Layout des Spiels nicht her).
Ich möchte also mit Hilfe der 12 verfügbaren Buchstaben alle die möglichen Lösungswörter finden, die sich mit diesem Satz bilden lassen. Als zusätzliche Einschränkung sollen hiervon wiederum nur diejenigen gewählt werden, bei denen die Anzahl der Buchstaben des Lösungsworts übereinstimmt. Die möglichen Lösungswörter sollen als eine Liste von Wörtern zur Verfügung stehen, damit wir nicht irgendwelche Lexika wälzen oder referenzieren müssen und dabei so viele mögliche oder gar unsinnige Lösungsvorschläge entstehen können, die das Spiel selbst sowieso (noch) nicht beinhaltet.
Auch hierbei kann es noch passieren, dass zu einem Buchstabensatz mehrere Lösungswörter passen, aber die Menge ist dann auf jeden Fall auf einen Blick überschaubar und kann dann mit Hilfe der Bilder vom Spieler leicht weiter eingeschränkt werden. In ganz harten Fällen, wo selbst der Bildinhalt eine der mehrfach vorgegebenen Lösungsmöglichkeiten nicht preisgibt, muss halt das altbewährte Try-and-Error herhalten. Mit dem eingeschränkten (zur Auswahl passenden) Satz an Vorschlägen ist das aber durchaus weniger aufwändig als sämtliche Wörter auszuprobieren oder gar alle 12 Buchstaben zu permutieren.
In den Weiten des Internets habe ich diese Seite gefunden, die auf einer HTML-Seite eine komplette Tabelle mit allen möglichen Lösungen enthält: touchportal.de/4bilder/4-bilder-1-wort-komplettlosung/
Start
Irgendwo müssen wir mit dem Projekt ja beginnen, also starten wir die Entwicklungsumgebung und legen ein neues Windows Forms Projekt an. Um die Form kümmer ich mich aber erst später. Jetzt gehts erstmal um die Datenbasis.
Wortliste
Um also Wörter finden zu können, brauchen wir eine Liste aller möglichen Lösungen. Auf den ersten Blick bietet sich der Typ List(Of String) an. Mir schwebte aber eher eine Liste vor, die direkt auch eine Methode zum Finden der Wörter bereithält. Und das was wir an Informationen für die Suche haben, hilft der List(Of T) leider nicht bei der Suche. Aus den drei offensichtlichen Ansätzen
- Eine Funktion schreiben, die mit der List(Of String), den 12 Buchstaben und der Wortlänge gefüttert wird und eine List(Of String) zurückgibt
- die Funktion aus (1) als Erweiterungsfunktion programmieren
- eine eigene Klasse aus List(Of String) ableiten und dort die Suchfunktion einbauen
Die Funktion
MatchingWords
erwartet dabei einen beliebig langen Satz an Buchstaben (es können hier auch weniger oder auch mehr als 12 Buchstaben sein) und optional die Anzahl der Buchstaben des gesuchten Worts. Wird die Anzahl nicht angegeben, sollen alle passenden Wörter unabhängig von deren Länge ermittelt werden. Das Ergebnis ist natürlich wiederum eine Wortliste.VB.NET-Quellcode
- Public Class Wordlist
- Inherits List(Of String)
- Public Function MatchingWords(ByVal availableChars As String, Optional ByVal wordLength As Integer = 0) As Wordlist
- Dim result As New Wordlist()
- For Each word As String In Me
- If (wordLength = 0 OrElse word.Length = wordLength) Then
- Dim found As Boolean = True
- If Not String.IsNullOrEmpty(availableChars) Then
- Dim availCharsCopy As String = availableChars.ToUpperInvariant()
- For Each c As Char In word.ToUpperInvariant()
- Dim p As Integer = availCharsCopy.IndexOf(c)
- If (p < 0) Then
- found = False
- Exit For
- End If
- availCharsCopy = availCharsCopy.Remove(p, 1)
- Next
- End If
- If found Then
- result.Add(word)
- End If
- End If
- Next
- Return result
- End Function
- End Class
Wortliste füllen
Bevor wir überhaupt irgendein Ergebnis bekommen, muss natürlich eine Wortliste zur Verfügung stehen, die mit den passenden Lösungen gefüllt ist. Da die Wörter alle auf einer Webseite zur Verfügung stehen, die zudem (zumindest zurzeit) bei Rätsel-Updates relativ zeitnah aktualisiert wird, bietet es sich an, diese jeweils direkt von dort zu beziehen.
Da ich mir die Scherereien mit schlecht programmiertem HTML oder XML schenken will, benutze ich keine Klasse zum Interpretieren solcher Dokumente, sondern wusel mich auf String-Ebene durch den Quelltext. Die fragliche Seite stellt die Lösungen in einer HTML-Tabelle dar, wobei alle Lösungswörter selbst in Tabellenzellen (
<td>
) stehen, die mit dem Attribut class="column-5"
versehen sind.Als zusätzliche Schwierigkeit kommt hinzu, dass hier manchmal zwei alternative Lösungen in einer Zelle stehen - vermutlich weil der Anbieter der Lösung nicht mehr genau wusste, ob nun z.B. "Disaster" oder "Desaster" die richtige Lösung war. Zum Glück folgt er dabei (bisher) immer demselben Schema, dass zwei Wörter durch "/" voneinander getrennt sind. Wir nehmen der Einfachheit halber beide mit auf und überlassen es dem zur Verfügung stehenden Buchstabensatz bzw. der Intelligenz des Benutzers, zu entscheiden, was nun tatsächlich die richtige Lösung ist.
Desweiteren gibt es - aus einem mir unerfindlichen Grund - in der Tabelle einige Lösungen, die mit "Level: xxx" oder "Level xxx" angezeigt werden. Das möchte ich nach Möglichkeit ebenfalls filtern.
Als Ergebnis kam eine Funktion heraus, die die Informationen aus dem Web lädt, die Wörter wie beschrieben herausfiltert und als Wortliste zur Verfügung stellt.
Da sie direkt mit der Klasse
Wordlist
in Abhängigkeit steht und eine Instanz davon zurückgibt, fand ich es logisch, sie als Shared-Funktion dieser Klasse zu implementieren:VB.NET-Quellcode
- Public Class Wordlist
- Inherits List(Of String)
- '...
- Public Shared Function LoadFromWeb() As Wordlist
- Const WEB_ADDRESS As String = "http://touchportal.de/4bilder/4-bilder-1-wort-komplettlosung/"
- Dim result As New Wordlist()
- Dim wc As New Net.WebClient()
- Using inStream As IO.Stream = wc.OpenRead(WEB_ADDRESS)
- Using reader As New IO.StreamReader(inStream)
- Do
- Dim line As String = reader.ReadLine()
- If line Is Nothing Then
- Return result
- End If
- If Not String.IsNullOrEmpty(line) Then
- line = line.ToUpperInvariant() 'alles in Großbuchstaben wandeln
- Dim p As Integer = -1
- Do
- p = line.IndexOf("<TD CLASS=""COLUMN-5""", p + 1)
- If p >= 0 Then
- Dim subLine As String = line.Substring(p)
- Dim p2 As Integer = subLine.IndexOf(">"c)
- If p2 >= 0 Then
- subLine = subLine.Substring(p2 + 1)
- p2 = subLine.IndexOf("<"c)
- If p2 >= 0 Then
- Dim solutionText As String = subLine.Substring(0, p2)
- If solutionText <> "LEVEL" Then
- solutionText = solutionText.Replace("LEVEL", "")
- End If
- Dim solutions As String() = solutionText.Split(" :/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)
- For Each wort In solutions
- If Not result.Contains(wort) Then
- result.Add(wort)
- End If
- Next
- End If
- End If
- End If
- Loop Until p < 0
- End If
- Loop
- End Using
- End Using
- Return result
- End Function
- End Class
Damit sind alle Voraussetzungen erfüllt und wir können uns an die Form machen.
MainForm
So einfach wie möglich: Eine TextBox als Eingabefeld. Wir setzen - finde ich persönlich gefälliger - die Eigenschaft
CharacterCasing
auf Upper
und MaxLength
auf 12. Alles andere ist mehr oder weniger "egal".Für die Auswahl, wieviele Buchstaben das Lösungswort haben sollte, habe ich eine Reihe aus 8 RadioButtons vorgesehen, jeweils beschriftet mit "Alle" und den Zahlen "2" bis "8". Ich hasse es, sowas von vornherein auf die Form zu klatschen, also platzier ich nur einen Button und lasse die restlichen 7 dynamisch generieren, sozusagen als Kopie des vorgegebenen, wobei der vorplatzierte praktisch die Position vorgibt und alle anderen sich dahinter reihen sollen.
Zu guter Letzt wollen wir auch die Ergebnisse irgendwo sehen, also setzen wir eine beliebige Listbox irgendwo hin. Die muss ja nur anzeigen. Mehr nicht.
Im Form_Load Event richten wir das alles soweit her, d.h. wir besorgen uns die Wortliste aus dem Web und erzeugen sieben Kopien unseres Buttons. Den Click-Handler weisen wir auch gleich zu, und damit wir uns nicht von Meldungen im Fehlerfenster nerven lassen müssen, legen wir dafür euch gleich den Rumpf mit an:
VB.NET-Quellcode
- Public Class Form1
- Private _allSolutions As Wordlist
- Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
- _allSolutions = Wordlist.LoadFromWeb()
- TextBox1.MaxLength = 12
- TextBox1.CharacterCasing = CharacterCasing.Upper
- ' Eventhandler für den ersten Button einrichten
- Dim refButton As RadioButton = Button1
- AddHandler refButton.Click, AddressOf Me.ButtonClick
- 'Buttons "2" bis "8" erzeugen und am jeweils vorherigen Button ausrichten
- For i As Integer = 2 To 8
- Dim btn As RadioButton = New RadioButton()
- btn.Appearance = refButton.Appearance
- btn.AutoSize = True
- btn.Location = New Point(refButton.Right + 1, refButton.Top)
- btn.Name = String.Format("btn{0}", i)
- btn.Text = i.ToString()
- AddHandler btn.Click, AddressOf Me.ButtonClick
- Me.Controls.Add(btn)
- 'neuen Button für den nächsten als Referenz merken
- refButton = btn
- Next
- 'alle Tastatureingaben zunächst zum KeyPress-Handler der Form umlenken
- Me.KeyPreview = True
- 'TODO:
- 'Jetzt kann man - damit es schön ist - noch Breite von Text- und ListBox an die Buttonzeile anpassen
- 'und die Breite der Form ändern, damit alles schön sichtbar ist und nicht so "hingerotzt" aussieht.
- End Sub
- Private Sub ButtonClick(sender As Object, e As EventArgs)
- ' kommt noch...
- End Sub
- End Class
Buttons per Taste
Bei den ersten Versionen meines Programms hatte mich dann aber doch eine Kleinigkeit gestört. Hatte ich brav meine 12 Buchstaben abgetippt und wollte nun die Auswahl auf Wörter bestimmter Länge begrenzen, musste ich mit der rechten Hand von der Tastatur auf die Maus wechseln um den Button zu klicken. Alternativ hätte ich mehrere TABs und ENTER nehmen können, aber das ist doch viel zu viel Tastengefrickel. Auch Shortcut-Keys, die ich dann mit Alt+irgendwas erreiche war mir zu viel Verrenkung, immerhin gebe ich ins Textfeld auschließlich Buchstaben ein. Wieso also nicht die Zahlen für den Direktzugriff auf die Buttons missbrauchen?
So kam es, dass im vorigen Code-Abschnitt sich die Zeile "Me.KeyPreview = True" eingeschlichen hat.
Um die Tasten dann auch entsprechend abzufangen, überschreibe ich einfach die OnKeyPress-Methode der Form-Klasse. Dort werden nicht nur die Tasten "0" und "2" bis "8" auf die entsprechenden Button-Clicks umgelenkt, sondern ich fange zudem die Escape-Taste ab, um das Programm mit Hilfe eines Tastendrucks beenden zu können. Ich liebe solche Shortcuts
VB.NET-Quellcode
- Protected Overrides Sub OnKeyPress(e As System.Windows.Forms.KeyPressEventArgs)
- Dim key As Short = Convert.ToInt16(e.KeyChar)
- If key = Keys.Escape Then
- Me.Close()
- Else
- Dim btn As RadioButton = Nothing
- If key = Keys.D0 Then
- btn = Button1
- ElseIf key >= Keys.D2 And key <= Keys.D8 Then
- btn = CType(Controls.Item(String.Format("btn{0}", e.KeyChar)), RadioButton)
- End If
- If btn IsNot Nothing Then
- e.Handled = True
- btn.PerformClick()
- Return
- End If
- End If
- MyBase.OnKeyPress(e)
- End Sub
Natürlich könnte man hier aus der gedrückten Taste gleich die Wortlänge ermitteln und die Listbox entsprechend füllen, aber es ging mir hierbei darum, exakt das Auslösen des Buttons mit der entsprechenden Zifferntaste zu bewirken. Wollte ich irgendwann das Verhalten eines Buttons ändern, müsste ich dann Code an zwei unterschiedlichen Stellen ändern. Das halte ich für ineffizient. Darum der Umweg über den Button und
PerformClick()
.Lösungen suchen (und finden)
Das wichtigste fehlt ja eigentlich noch: Nämlich die Verknüpfung der eingegebenen Buchstaben mit der Suchfunktion. Das erledigen wir einfach im TextChanged-Event der Textbox, so sehen wir ggf. schon während der Eingabe der verfügbaren Buchstaben, was eventuell als Lösung in Betracht kommen könnte. Da wir über die Buttons später steuern wollen, auf wieviel Buchstaben die Lösungswörter beschränkt sein sollen, benutzen wir dafür eine eigene Variable auf Klassenebene.
Restliche Logik
Fehlen noch die eigentlichen Events für Button.Click und TextBox.TextChanged:
VB.NET-Quellcode
- Private Sub TextBox1_TextChanged(sender As System.Object, e As System.EventArgs) Handles TextBox1.TextChanged
- FindAndDisplay()
- If TextBox1.Text.Length = 12 Then
- TextBox1.SelectAll()
- End If
- End Sub
- Private Sub ButtonClick(sender As Object, e As EventArgs)
- Integer.TryParse(CType(sender, RadioButton).Text, _wordlength)
- FindAndDisplay()
- End Sub
Das Integer.TryParse macht aus dem Text "Alle" des ersten Buttons automatisch den Wert 0. Das ist recht praktisch, weil das genau der Wert ist den wir brauchen, um die Lösungsliste nicht auf eine bestimmte Wortlänge einzuschränken.
Was fehlt?
Ein paar Kleinigkeiten kann man sicher noch verbessern. Zum einen das Layout, wie an der entsprechenden Stelle aber schon angemerkt.
Dann ist mir aufgefallen, dass wenn man mit seinen Wurstfingern statt eine der Ziffern 2 bis 8 oder 0 doch mal die 1 oder 9 erwischen sollte, dass diese dann doch wieder an das Textfeld geht. Das kann man aber auch noch abfangen. (Hausaufgabe! )
Dadurch dass bei jedem Programmstart die Webseite abgerufen und analysiert wird zieht der Start sich für so eine Pipifax-Anwendung ziemlich hin. Eine Verbesserung könnte es sein, die Daten aus dem Web nur auf spezielle Anforderung (Button?) zu laden und dann lokal in einer Datei oder Datenbank zu speichern, die stattdessen verwendet wird.
Vielleicht kennt und nutzt ja der eine oder andere von euch die App "4 Bilder 1 Wort" und findet mit der Hilfe dieses Tools endlich die Lösung, an der er seit Wochen festhängt und deshalb nicht weiterspielen kann?
Weltherrschaft erlangen: 1%
Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
Danke.
Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
Danke.
Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Arby“ () aus folgendem Grund: Projektdateien hinzugefügt