lehrkraefte:blc:informatik:ffprg1-2022:wordle:start

Wordle

Das Ziel ist es, ein kleines Wordle-Spiel zu programmieren.

Dazu werden wir einzelne Programmteile erstellen, die dann am Schluss zu einem Ganzen zusammengefügt werden können.

Wir werden für unsere Funktionen immer gleich auch Tests schreiben, so haben wir eine Chance, logische Fehler früh zu erkennen.

Die Funktion richtig(a,b) liefert ein Array mit Einträgen True oder False, je nachdem ob der Buchstabe in den Strings a und b an der entsprechenden Stelle übereinstimmt.

  • Input: Zwei Strings, (in den Variablen solution und guess).
  • Programmabbruch mit Fehlermeldung, wenn die Länge von solution und guess nicht übereinstimmt.
  • Output: Array mit Einträgen True oder False
def richtig(solution,guess):
  if len(solution)!=len(guess):
    raise ValueError("Länge der beiden Wörter '%s' und '%s' stimmen nicht überein." % (solution,guess))
 
  # Resultat Array bauen und in der Variablen res speichern
  res = []
  #
  # ...
  #
  return res    # Resultat zurückgeben und Funktion beenden
 
 
def tests():
  cases = [["HABER", "RABEN", [False, True, True, True, False]],\
           ["TAGEN", "RABEN", [False, True, False, True, True]],\
     ]
  for test in cases:
    res = richtig(test[0], test[1])
    if res!=test[2]:
      raise ValueError("richtig(%s, %s) sollte %s liefern, liefert aber %s" % (test[0], test[1], str(test[2]), str(res)))
  print("Alle Tests erfolgreich!");
 
 
tests()
  • Vervollständigen Sie die Funktion richtig
  • Fügen Sie weitere Tests hinzu.

Wie oben, schreiben Sie eine Funktion bewerten(versuch, geheim), die ein Array 0 (schwarz), 1 (gelb) und 2 (grün) zurückgibt.

Es gibt zwei Videos, die den Algorithmus erklären. Versuchen Sie erst, die Aufgabe ohne die Videos zu lösen. Wenn das nicht klappt, schauen Sie sich nur den ersten Video an und versuchen Sie dann, die Methode in Python umzusetzen. Wenn das auch nicht klappt, schauen Sie sich das zweite Video auch noch an, schreiben Sie den Code aber nicht aus dem Video ab, sondern versuchen Sie, diesen Code danach selbst zu schreiben.

Die Funktion sollte folgende Tests erfüllen (erfinden Sie gerne eigen, weitere Tests!)

def tests():
  cases = [["HABER", "RABEN", [0,2,2,2,1]],\
           ["TAGEN", "RABEN", [0,2,0,2,2]],\
           ["AABBB", "BAAAB", [1,2,1,0,2]],\
           ["AAABB", "BAAAB", [1,2,2,1,2]],\
     ]
  for test in cases:
    res = bewerten(test[0], test[1])
    if res!=test[2]:
      raise ValueError("richtig(%s, %s) sollte %s liefern, liefert aber %s" % (test[0], test[1], str(test[2]), str(res)))
  print("Alle Tests erfolgreich!");

Erklärvideos:

print("\033[42mThis is green\033[0m")
print("\033[43mThis is yellow\033[0m")
  • Variante «easy»: Wortliste aus dem JavaScript-Code eines online Wordle kopieren.
  • Variante «suchen und finden»: Wortliste auf dem Web suchen.
  • Variante «selber bauen»: Wikipedia-Artikel herunterladen, Wörter extrahieren, nach Häufigkeit sortieren.

Wikipedia «downloaden»

Folgender Code funktioniert mehr schlecht als recht. Besser wäre es wohl, eine Library oder externes Programm wie 'curl' zu verwenden, das würde das ganze viel stabiler machen. Man könnte wohl auch wget verwenden und gleich die ganzen Webseiten speichern und dann diese offline bearbeiten.

Der Vorteil dieses Programm ist, dass man die Wörter gleich mit der Häufigkeit hat. So kann man nur die häufigsten Wörter als zu erratende Wörter zulassen, aber alle als gültige Eingabewörter.

Code

Code

crawler.py
import socket 
import ssl
import urllib
import re
import os
import sys
 
# Or simply set this to True in TigerJython
tigerJython = (sys.executable.find("jython")>=0)
 
# Download einer Wikipedia-Seite
# URL ist z.B. /wiki/Burg_Liechtenstein
def download(url):
    host = "de.wikipedia.org"
    port = 443
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(0.5)
    s.connect((host , port))
    s = ssl.wrap_socket(s)
    request = "GET " + url + " HTTP/1.1\r\nHost: " + host + "\r\n\r\n"
    if (tigerJython):
        s.sendall(request)
    else:
        s.sendall(bytes(request, 'utf-8'))
    try:
        reply = s.recv(4096)
    except socket.timeout:
        s.close()
        return ""
    start = reply.decode('utf-8').find("Content-Length: ");
    if (start==-1):
        start = reply.decode('utf-8').find("content-length: ");
    if (start>=0):
        end = reply.decode('utf-8').find("\r\n",start)
        #while reply[end]>='0' and reply[end]<='9':
        #    end+=1
        # print("Length: ->"+reply[(start+16):end]+"<-")
        numbytes = int(reply[(start+16):end])
        print("About to get %d bytes" % numbytes);
        while reply.decode('utf-8').find("\r\n\r\n")<0:
            # print("About to get next chunk...")
            more = s.recv(4096)
            # print("Got %d more bytes" % len(more))
            reply += more
        reply = reply[(reply.decode('utf-8').find("\r\n\r\n")+4):]
        # print("Reply so far: "+reply)
        while len(reply)<numbytes:
            # print("About to get next chunk...")
            more = s.recv(4096)
            reply += more
            # print("Len reply = %d, len more = %d" % (len(reply), len(more)))
 
    else:   
        # print("Could not find Content-Length in ")
        # print(reply)
        try:
            while True:
                # print(".")
                more = s.recv(4096)
                reply += more
        except socket.timeout:
            pass
    s.close()
    return reply.decode('utf-8', 'ignore')
 
 
todo = ["/wiki/Neoklassische_Synthese"]
words = {}
linksdone = {}
# Load existing data, if any
for l in range(4,11):
    datafile = "wortliste%02d.txt" % l
    if os.path.isfile(datafile):
        with open(datafile, "r") as file:
            for line in file.readlines():
                entries = line.split(" ")
                words[entries[0]] = int(entries[1])
 
 
 
for i in range(200):
    url = todo.pop(0)
    linksdone[url] = True
    print("Downloading "+url)
    html = download(url)
    links = re.findall(r'<a href="(/wiki/.*?)"', html)
    links = [l for l in links if re.search('[%:#]', l)==None and (not l in linksdone)]
    for l in links:
        if not l in linksdone:
            linksdone[l]=True
            #print(l)
            todo.append(l)
 
    text = re.sub('<script>.*?</script>', "", html)
    text = re.sub('<.*?>', "", text)
    ww = [w.upper() for w in text.split() if re.match('^[A-Z]{4,10}$',w.upper())]
    # print("Have %d words" % len(ww))
    newwords = 0
    for w in ww:
        if w in words:
            words[w]+=1
        else:
            newwords+=1
            words[w] = 1
 
    print("Have %d new words" % newwords)
 
print("Open pages:")
print(todo[0:10])
 
alle = words.keys()
# print("\n".join(alle))
for l in range(4,11):
    wl = [w for w in alle if len(w)==l]
    wl.sort()
    # print("\n".join(wl))
    with open("wortliste%02d.txt" % l, "w") as file:
        for w in wl:
            file.write("%s %d\n" % (w, words[w]))

Ein mögliches Resultat dieses Codes ist wortliste05.txt.

Eine riesige Liste (1.2 Mio) deutscher Wörter gibt es hier: https://sourceforge.net/projects/germandict/ Diese habe ich gefiltert und folgende Liste extrahiert: 6308 Wörter. Der Filter ist hier beschrieben.

Brython erlaubt es, Python auf Webseiten einzusetzen. Hier sind die nötigen Funktionen einmal programmiert. Was noch fehlt, ist die Spiel-Logik:

https://ofi.tech-lab.ch/2022/teach/d0571f1e/brython/

Hinweis: Öffnen Sie die Developper-Tools im Browser (F12). Im Konsolen-Tab sehen Sie die print-Ausgaben. Das kann bei der Fehlersuche helfen.

  • lehrkraefte/blc/informatik/ffprg1-2022/wordle/start.txt
  • Last modified: 2022/06/02 12:19
  • by Ivo Blöchliger