Fortführung anderer Thread / zahl_erraten.py

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

BSA hat geschrieben:Beim ausprobieren ist mir allerdings noch ausgefallen: bei 'print(math.ceil(math.log(100)))' bekomme ich als Ergebnis eine '5' - ?
Das ist richtig. Ich kann mich an den Inhalt des Threads nicht mehr so genau erinnern, aber da es um Bisektionsverfahren geht, sollte es wohl eher ``math.ceil(math.log(100, 2))`` heißen.
BSA hat geschrieben:Zur Funktion bisect() muss ich mir jedoch leider eingestehen, dass ich die Anweisung 'int(math.ceil(math.log(x)' (mathematisch) jetzt grade nicht auf Anhieb verstehe...meine Schulzeit ist schon ein paar Tage her.
Das ist doch jetzt nicht so schwer zu verstehen: math.log(x) ist der natürliche Logarithmus. Wenn du eine Gleichung y=e^x hast und auf beiden Seiten log anwendest, dann bekommst du den Wert für x heraus, damit e^x den Wert y ergibt. Im Fall von log2, also dem oben genannten log(x, 2), lautet die Gleichung y=2^x. Wendest du den log2 darauf an bekommst du heraus, wie oft du zwei mit sich selbst multiplizieren musst, damit y heraus kommt. Der Zusammenhang mit dem Bisektionsverfahren sollte dir jetzt auch klar werden, da du in jedem Schritt die Anzahl der möglichen Elemente halbierst. Angenommen, du hast 64 Elemente. Jetzt schaust du dir die Mitte an und testest, ob der gesuchte Werte links oder rechts liegt. Liegt er rechts, bleiben noch die linken 32 übrig, liegt er rechts, dann die rechten 32. Mit den 32 gehst du jetzt genau so vor und kommst dann auf 16, 8, 4, 2 und anschließend auf 1. Insgesamt machst du also 6 Halbierungen: 64->32, 32->16, ..., 2->1. Und jetzt überlege mal was 2^6 ist und was genau das mit den 64 Ausgangselementen zu tun hat ;-)

Der Rest ist einfach: ceil rundet einfach nur auf. Angenommen, du hast 65 Elemente, dann sind (der Formel nach) etwas mehr als 6 Teilungen nötig. Da du aber keine Bruchteile einer Teilung durchführen kannst sondern entweder teilst oder gar nicht, wird eine zusätzliche Teilung notwendig. Daher wird dann auf sieben aufgerunden und schon stimmt est wieder.
Das Leben ist wie ein Tennisball.
BlackJack

@BSA: Verdammt, ich habe beim `log()` das zweite Argument vergessen, man will hier natürlich nicht den natürlichen Logarithmus sondern den zur Basis 2:

Code: Alles auswählen

In [4]: print(math.ceil(math.log(100, 2)))
7.0
Zwischen Deiner Variante und der geschlossenen Formel gab es doch noch die Schleife die zählt wie oft man durch zwei Teilen muss bis das Ergebnis ≤1 ist. Das sollte sich relativ leicht nachvollziehen lassen. Man hat am Anfang x Kandidaten und rät den in der Mitte. Wenn man falsch lag hat man noch x/2 Kandidaten übrig für den nächsten Versuch. Welche Hälfte das ist, ist für die Bestimmung der nötigen Versuche ja egal. Man zählt einfach wie oft man den Suchraum halbieren kann bis 1 oder kleiner heraus kommt. Spätestens dann ist man bei dem Element angekommen welches geraten werden soll. Ungetestet:

Code: Alles auswählen

def calculate_max_trials(n):
    for i in itertools.count(1):
        if n <= 1:
            return i
        n /= 2
Zum Logarithmus: log(x, b) ist die Umkehrfunktion zu b**x. Also während ``x = 2**n`` uns sagt was man bekommt wenn man 2 n-mal multipliziert, sagt ``n = log(x, 2)`` wie oft man x durch 2 teilen kann. Bei Zweierpozenten geht das natürlich glatt auf. Ansonsten bekommt man einen Nachkommaanteil. In dem Fall muss man dann auf die nächste ganze Zahl aufrunden → `ceil()`.

Das mit der Funktion zum Testen ob die Sounddateien ladbar sind ist keine schlechte Idee. Da könnte man als Rückgabewert ein Wörterbuch erstellen das die Sound/Ereignisnamen auf die Dateinamen abbildet beziehungsweise auf `None` falls die entsprechende Datei nicht geladen werden konnte. Und dieses Wörterbuch kann man dann einer `play()`-Funktion übergeben, zusammen mit dem Soundnamen der abgespielt werden soll. So kann man das `sound_on`-Flag loswerden, weil man im Falle das der Benutzer keinen Sound haben möchte ganz einfach so ein Wörterbuch erstellen kann bei dem alle Soundnamen auf `None` abgebildet werden. Damit wird das Programm ein wenig einfacher und ein paar Zeilen fallen weg. *Dann* kann man vielleicht auch mal über eine sinvolle Klasse für den Sound nachdenken.

Bei den Flags fällt noch auf das Du 0 und 1 statt `False` und `True` verwendest. Letzteres würde es dem Leser deutlicher machen das es nur zwei Werte gibt und nicht auch 2 oder eine andere Zahl in Frage käme.
BSA
User
Beiträge: 38
Registriert: Freitag 6. Dezember 2013, 07:49

Moin,

zuerst mal zur Formel 'int(math.ceil(math.log(x, y)))' :
Da merke ich einschneidend, dass ich damals in der Schule wohl nicht allzu gut aufgepasst habe... Allerdings, soweit ich mich erinnern kann, haben wir Logarithmik damals in der 10. auch schon gar nicht mehr gehabt...
Danke für eure Erklärungen, ich denke, jetzt sollte soweit alles klar sein. Man stellt sich alles immer schwerer vor als es in Wirklichkeit vielleicht ist...
log(81, 3) würde (in diesem Fall) also eine glatte 3 ergeben. Wobei log(82, 3) als Ergebnis 3.037037... ergeben würde, weswegen dann mit der Funktion ceil() jede angefangene neue Zahl (0.00 bis 1.00 = 1. natürliche Zahl, 1.00 bis 2.00 = 2. nZ, 2.00 bis 3.00 = 3. nZ, 3.00-4.00000...1 = 4. "angebrochene Zahl" -> über ceil() interpretiert also = 4) auf die bereits angebrochene nächste Zahl aufgerundet wird - toll!
Folglich ist jede natürliche Zahl bis 1024 mit y = e^10 zu ermitteln, wobei für eine Zahl bis 2048 e^11 oder y = 11 gilt - das ist wirklich interessant, dass so mal zu sehen.
Ich glaube, ich werde den Begriff "Logarithmik" so schnell also nicht mehr vergessen, schön! Wofür Python alles gut ist...! ;-)
Ich muss jedoch auch hier noch kurz gestehen, dass ich bei meiner eigens erstellten Funktion (der lange Weg...) an das potenzieren überhaupt nicht gedacht habe, was sich wahrscheinlich an der Funktion bisect() auch einfach ablesen lässt.
Die Funktion über itertools.count habe ich schnell mal getestet. Rausgekommen ist bei mir für jede Berechnung im Endeffekt 'y = x**(b+1)', sprich für den Bereich 128 kam y = 8 heraus, ebenso für 127.Ich habe die Funktion dann "fachmännisch" folgendermaßen abgeändert, wobei das Ergebnis dann jedoch richtig ausgegeben wurde:

Code: Alles auswählen

def calculate_max_trials(n):
    for i in itertools.count(1):
        if n <= 1 + 1:
            return i
        n /= 2
Damit war das Ergebnis dann richtig. Für n = 128 also mathematisch ausgedrückt -> y = 2^7; für n = 129 -> y = 2 ^ 8, wobei 7 bzw. 8 hier jeweils 'b' sind, also das gesuchte Ergebnis.
Da ich das alles kurz im Terminal durchgeführt habe, ist es nicht als Datei gespeichert.
[Entwurf zwischengespeichert bis hierher: heute Nachmittag bei der Arbeit]

Ich habe mir eben noch kurz die englischen Documents auf python.org für itertools angeschaut, dort insbesondere count(). Daraufhin habe ich die Funktion folgendermaßen abgeändert:

Code: Alles auswählen

def calculate_max_trials(n):
    for i in itertools.count(0, 1):
        if n <= 1:
            return i
        n /= 2
Auf die beiden Parameter für .count bin ich über die Doc's gestoßen, sie stehen (was ich euch sicherlich nicht zu erzählen brauche...) für start = 0 und step = 1. Python hat .count(1) vermutlich als .count(start=1) interpretiert. Was dann für step genommen wurde, entzieht sich meiner Kenntnis, evtl. die 0 ? Ich weiß es allerdings nicht...

Meine `load_sounds()' - Funktion habe ich noch nicht ganz fertig. Die anderen Änderungen lade ich hier aber schonmal Funktionsweise hoch. Wo die Änderung minimal, die Funktion aber größer war, nenne ich kurz die Änderung.
___________________________________________________________________

ELSE_STR & EXCEPT_STR beginnen nun bei 0 und sind nummeriert bis 3. :oops:

Code: Alles auswählen

def random_str(dictionary):
    '''Diese Funktion dient zur Zufallsausgabe eines else / except - Strings.

    Als Grundlage dienen zwei Listen. Die Auswahl einer Liste erfolgt über
    den Parameter (dictionary).'''

    if dictionary == 1:
        string = random.choice(ELSE_STR)
    elif dictionary == 2:
        string = random.choice(EXCEPT_STR)
    print(string)

Code: Alles auswählen

def text_render(fast_text, beg, end):
    '''Liest eine Datei ein und gibt die benötigten Zeilen wieder.

    Zeilen sind einzeln ab 0 aufzurufen.
    Die letzte auszugebende Zeile in einem Block ist stets um den Wert 1 zu
    erhöhen.'''

    if fast_text == 1:
        delay = 0.000064
    else:
        delay = 1.6
    try:
        with open("guess.txt", "r") as textfile:
            text_lines = textfile.read().split("\n")
            li = []
            for line in text_lines:
                if line:
                    sep_list = line.split(";")
                    li.append(sep_list[0])
            for line in range(beg, end):
                print(li[line])
                time.sleep(delay)
    except IOError:
        textfile = 0
        print()
        print()
        print('''Dateizugriff nicht erfolgreich!
        Hinweis: Datei "guess.txt" fehlt!
        Programm wird beendet ...''')
        time.sleep(3)
        quit()
    finally:
        if textfile is True:
            textfile.close()
        else:
            print()
^-- Hier bin ich mir nicht sicher, ob das nun richtig ist, sprich ob ich nun direkt über textfile iteriert habe, oder ob ich das ganze so umschreiben soll, dass die Liste gar nicht mehr gebraucht wird ... :?:


'Import math'
'from math import ceil, log'
Die Funktion 'bisect(cipher_band)' habe ich erstmal so stehen lassen, alle Zeilen bis auf den DocString jedoch gelöscht und dafür die Formel eingefügt: 'return int(math.ceil(math.log(cipher_band, 2)))'


Die sound_on Flags hätte ich im Grunde auch noch gleich auf True / False geändert, habe ich aber noch nicht. Kommt natürlich noch.
Bzw., ich arbeite vorerst noch an meiner eigenen Idee bzgl. der load_sounds(), die ist zwar "ein wenig" komplizierter als der von dir vorgeschlagene Weg (@BlackJack), und benötigt auch die Flags für sound_on...
Sobald die aber steht und *richtig* (= wie gewollt) funktioniert, werde ich wohl deinen Vorschlag umsetzen. Der scheint wohl um einiges "leichter" (und sinnvoller...) zu sein. Dennoch möchte ich gerne erst noch meine eigene Idee mal wenigstens kurz zum funktionieren bringen. :-)

Wie gesagt, an der 'load_sounds'-Funktion arbeite ich noch. Wenn die steht, werde ich die 'play_sound'-Funktion noch miteinbauen. Die Baustelle ist aber vorerst die 'load_sounds'. Ich denke, insgesamt sollte es jedoch machbar sein. Vorschweben tut mir auch (sobald die Ausgabe richtig funktioniert, momentan wird zwar der erste Dict-Value bei fehlen einer Datei ausgegeben, jedoch ist das natürlich nicht immer auch der richtige / einzige...), die fehlende Datei bzw. die fehlenden Dateien in eine .txt zu schreiben, die im Falle eines except pygame.error in der 'load_sounds' gleich mitgeneriert wird.
Es ist zwar
1. naiv zu glauben, dass irgendwer mal das Spielchen wirklich interessant finden sollte, und es daher mal irgendwann von GitHub runterladen sollte, und
2. noch naiver zu glauben, dass es irgendwen interessieren würde, welche Datei denn da nun eigentlich fehlt und diese Datei dann im Internet suchen, runterladen und in seinen Ordner kopieren würde, Aber:

Darum geht es ja auch gar nicht ;-)
Es würde dann also bei einem fehlerhaften Laden irgendeiner Datei eine .txt im selben Ordner wo das Hauptprogramm wäre erstellt, welche einen vorgeschriebenen Text plus eine Fehlerausgabe enthielte, in der aufgelistet würde, welche Dateien enthalten und welche fehlen würden.
Außerdem könnte in dieses file auch reingeschrieben werden, wo die fehlende Datei nachzubesorgen ist :-)
Dieses file würde natürlich nur erstellt, sofern tatsächlich das fehlen einer benötigten Datei festgestellt würde. Ansonsten merkt der Nutzer von all dem natürlich auch nichts.


edit:

Code: Alles auswählen

import pygame
import time
pygame.mixer.init()


SOUND_FILES = {'noticed': 'signatur-Dil-7491_hifi.mp3',
    'bkgrnd_people': 'People_T-Doogens-8720_hifi.mp3',
    'win': 'Applause-SFXsourc-9027_hifi.mp3',
    'check': 'plim-Gato_pre-7790_hifi.mp3'
    }
KEY_DICT = {0: 'signatur-Dil-7491_hifi.mp3',
    1: 'People_T-Doogens-8720_hifi.mp3',
    2: 'Applause-SFXsourc-9027_hifi.mp3',
    3: 'plim-Gato_pre-7790_hifi.mp3'
    }


while True:
    try:
        response_snd = int(input('SOUND aktivieren? [1] JA  |  [0] NEIN '))
        if response_snd == 1:
            try:
                pygame.mixer.music.load(SOUND_FILES['noticed'])
                pygame.mixer.music.load(SOUND_FILES['bkgrnd_people'])
                pygame.mixer.music.load(SOUND_FILES['win'])
                pygame.mixer.music.load(SOUND_FILES['check'])
                print('Sounddateien erfolgreich geladen ...')
                sound_on = 1
                time.sleep(0.4)
                break
            except pygame.error:
                print('Sounddatei(en) nicht gefunden !')
                keys = KEY_DICT.keys()
                key_dict_top = max(KEY_DICT)
                for key_nr in KEY_DICT:
                    try:
                        pygame.mixer.music.load(KEY_DICT[key_nr])
                    except pygame.error:
                        print('DATEI "{}" FEHLT !'.format(KEY_DICT[key_nr]))
                        print('''Behebung des Fehlers:
                        "{}" im WWW suchen ...'''.format(KEY_DICT[key_nr]))
                        print('Datei in den Spiel-Ordner kopieren ...')
                        time.sleep(3)
                        print('Spiel wird ohne Sound fortgesetzt ...')
                        sound_on = 0
                        time.sleep(3)
                        break
                break
        elif response_snd == 0:
            sound_on = 0
            break
        else:
            print('''Bestätige [1] um den Sound zu aktivieren
            [0] deaktiviert die Soundausgabe!''')
    except ValueError:
        print('''Bestätige [1] um den Sound zu aktivieren
            [0] deaktiviert die Soundausgabe ...''')
Ausgangslage:
eine Datei fehlt -> Dateiname wird richtig benannt
zwei Dateien fehlen -> Es wird lediglich der erste in der Liste erkannte fehlende Datei benannt. Der Rest steht im Code.
Ich habe auch eine andere Version, in der die Schleifenbearbeitung bei der ersten gefundenen fehlenden Datei stehen bleibt bzw. sich endlos an der Stelle wiederholt.
Sicher lässt sich das noch so modifizieren, so dass es läuft...also die erste, zweite und jede weitere fehlende Datei benannt wird. Ich habe da aber nun ein wenig dran gesessen und ich glaube, ich werde für heute Schluß machen und mich dann als nächstes an die von dir (@ BlackJack) vorgeschlagene Rangehensweise versuchen.
Da lohnt sich dann auch der Zeitaufwand, da es die anscheinend richtige Rangehensweise ist.
Mit Sicherheit würde ich es noch irgendwie hinbekommen, irgendwann würde es wohl wie gewollt funktionieren, aber: was bringt es mir? Ich werde am Ende eh auf die von dir vorgeschlagene Rangehensweise umswitchen, von daher also in erster Linie Zeitverlust.

Hab es nur mal schnell der Vollständigkeit halber gepostet. Der Code ist falsch und funktioniert nicht wie er eigentlich soll. Ich werde also auf die andere Methode mit der frisch zu erstellenden Liste (bei no_sound je sound_file = None) versuchen einzugehen. Mal sehen, wie's da läuft... Die Komplexität mit den SoundFlags umgehe ich damit, wie von dir beschrieben, ja auch direkt.
Oftmals beschleicht mich hier im Forum das heimliche Gefühl, an verschiedenen Stellen mal ein einfaches "Bahnhof" zu posten.

Wann du den Fisch auch fängst, er ist frisch. Sprichwort
BSA
User
Beiträge: 38
Registriert: Freitag 6. Dezember 2013, 07:49

Die Funktion 'load_sounds()' ist in der Rohfassung nun fertiggestellt.

Es sieht auf den ersten Blick vielleicht leicht verwirrend aus, wer sich aber etwas reinsetzt, wird sehen, wie der Ablauf gedacht ist.

Hier aber erstmal der Code :-)

Code: Alles auswählen

import pygame
import time
pygame.mixer.init()


VALUELIST_SOUNDFILES = ['signatur-Dil-7491_hifi.mp3',
    'People_T-Doogens-8720_hifi.mp3',
    'Applause-SFXsourc-9027_hifi.mp3',
    'plim-Gato_pre-7790_hifi.mp3'
    ]
KEYLIST_SOUNDFILES = ['noticed', 'bkgrnd_people', 'win', 'check']
SOUND_FILES = {}
SOUNDS_MISSING = {}


def load_sounds():
    '''Diese Funktion versucht benötigte Dateien zu laden.

    Wenn dies fehlschlägt, erstellt die Funktion einen Fehlerbericht und gibt
    dies nach Erfolg im Programm bekannt.
    Ist ein Fehlerbericht, z.B. aufgrund mangelnder Schreibrechte auf der HDD,
    nicht erstellbar, werden die erfassten Informationen dem Anwender am
    Bildschirm ausgegeben.
    Eine Nebenfunktion ist hier im Übrigen auch, dass der Anwender gefragt
    wird, ob die Dateien überhaupt geladen werden sollen.'''

    keys = KEYLIST_SOUNDFILES
    values = VALUELIST_SOUNDFILES
    number_keys = len(values)
    error_text = ['___FEHLERBERICHT FEHLENDE SOUNDS___\n',
    'GENERIERENDES PROGRAMM             :_GUESSY_', '\n' * 3,
    'Hinweis zur Fehlerbehebung:\n',
    'Fehlende Dateien aus dem WWW downloaden und in Ordner kopieren!',
    '\n' * 3, 'Folgende Datei(en) wurden nicht gefunden :', '\n']
    while True:
        print()
        try:
            response_snd = int(input('SOUND aktivieren? [1] JA  |  [0] NEIN '))
            if response_snd == 1:
                for i in range(number_keys):
                    try:
                        pygame.mixer.music.load(values[i])
                        SOUND_FILES[keys[i]] = values[i]
                        if i == number_keys:
                            print('Sounddateien erfolgreich geladen...')
                            time.sleep(0.64)
                            return SOUND_FILES
                    except pygame.error:
                        SOUNDS_MISSING[keys[i]] = values[i]
                for i in range(number_keys):
                    if len(SOUNDS_MISSING) > 0:
                        for i in range(number_keys):
                            SOUND_FILES[keys[i]] = None
                        print('{} Sounddatei(en) nicht gefunden !'.format(len
                        (SOUNDS_MISSING)))
                        try:
                            with open('guessy_missing_sounds.txt', 'w') as f:
                                f.writelines(error_text)
                                f.write('{}\n'.format(SOUNDS_MISSING))
                                f.write('\n\nAnzahl fehlender /oder/ '
                                'fehlerhafter Dateien: {}'.format(len
                                (SOUNDS_MISSING)))
                                f.close()
                        except IOError:
                            print('KEINE SCHREIBBRERECHTIGUNG !')
                            time.sleep(1)
                            print('Manuelle Ausgabe fehlender Dateien ...')
                            time.sleep(1)
                            print('{} Dateien fehlen oder sind beschädigt !'.
                            format(len(SOUNDS_MISSING)))
                            time.sleep(1)
                            print('Auflistung der Dateien folgt ...')
                            time.sleep(1)
                            print('{}'.format(SOUNDS_MISSING))
                            print('\n\Anzeige für 16 Sekunden ...')
                            time.sleep(16)
                            print('Fehlende Dateien aus dem WWW nachladbar ...')
                            return SOUND_FILES
                        print('''Hinweis :

                        FEHLERBERICHT : erstellt
                        ORT           : Haupt-Ordner
                        NAME          : guessy_missing_sounds.txt''')
                        time.sleep(2.4)
                        print()
                        print('Spiel wird ohne Sound fortgesetzt ...')
                        time.sleep(1)
                        return SOUND_FILES
                    else:
                        return SOUND_FILES
            elif response_snd == 0:
                for i in range(number_keys):
                    SOUND_FILES[keys[i]] = None
                print('Sound DEAKTIVIERT')
                time.sleep(0.3)
                return SOUND_FILES
            else:
                print('''Bestätige [1] um den Sound zu aktivieren
                [0] deaktiviert die Soundausgabe!''')
        except ValueError:
            print('''Bestätige [1] um den Sound zu aktivieren
                [0] deaktiviert die Soundausgabe ...''')

Wie immer ist der Code etwas länger geworden als erwartet. Ich wüsste allerdings jetzt grade auch nicht, wo sich noch großartig Ressourcen einsparen ließen.
Wenn zwischendurch noch vereinzelt ein 'print(xyz)' auffallen sollte, welches für den Anwender nicht gedacht ist - der Code ist noch recht frisch ;-)

Auch konnte ich die Ausnahme für den IOError nicht ausprobieren. Sicher ließe sich das irgendwie in meinem Linux bewerkstelligen (wenn nicht unter Linux, wo denn sonst...), habe ich aber noch nicht. Von daher kann ich nicht genau sagen, dass der Code an dieser Stelle funktioniert, gehe da aber von aus.


Wie immer, freue ich mich natürlich wieder über eure Kritik und Verbesserungsvorschläge!


Und Danke nochmal an Dich, BlackJack, für die wirklich gute Idee !
Die 'play_sound()' sollte ja nun kein großes Problem mehr darstellen. Die sound_on - Flags werden dann direkt mit der Implementierung der play_sound() rausfliegen :-D

Aktuell bin ich am überlegen, wie eine HighScore - Formel am saubersten und gerechtesten aussehen könnte. Sollte ich da etwas erarbeiten, gibt es zunächst die Ausgabe. Als nächsten Schritt würde dann evtl. eine default-Liste nach und nach durch den Anwender ersetzt werden können. Auch die Werte wären dann noch zu erproben oder -schätzen.
Aber das ist natürlich nur eine Idee. Ob und wie ich sie umsetze, weiß ich natürlich noch nicht. Mehr dazu, wenn, dann beim nächsten upload.

Es kann allerdings sein, dass bei mir einige wenige Tage Verschnaufpause angesagt ist, zwar nicht ganz freiwillig, aber doch schon irgendwo ;-)
Außerdem ist mir nach den ganzen Loops auch schon ganz schwindlig...(mal gucken, wie lang der Vorsatz hält :lol:)


edit: Eine Kleinigkeit vielleicht noch. Ich habe schon einiges durchforstet. In der guessy_missing_sounds.txt ist das gesamte Schlüsselpaar "Key/Value" des jeweiligen Dictionary-items nebst {''} anegzeigt. Ich habe vorhin einige Lösungswege probiert, diese jedoch nicht wegbekommen. Daher hab ich es erstmal so gelassen. Die Lösung ist wahrscheinlich trivial...aber heute werde ich mich wenn dann noch mit dem Umschreiben des eigentlich Programms (inkl. der play_sound() ) beschäftigen. Das ist ja dann auch später nur noch die eine Stellschraube an zwei Stellen (IOError und f.write)

Die beiden Listen plus das Dict MISSING_SOUNDS sind natürlich innerhalb der Funktion. Lediglich die SOUND_FILES ist global und ruft dann natürlich auch die Funktion an der entsprechenden Stelle auf. Das ist hier jetzt noch nicht so dargestellt.
Oftmals beschleicht mich hier im Forum das heimliche Gefühl, an verschiedenen Stellen mal ein einfaches "Bahnhof" zu posten.

Wann du den Fisch auch fängst, er ist frisch. Sprichwort
BlackJack

@BSA: Aus dem Schulunterricht könnte ich mich jetzt auch nicht mehr an den Logarithmus erinnern. Da wird man in der Informatik aber unweigerlich mit konfrontiert wenn man sich mit der Komplexität von Algorithmen in Abhängigkeit von Eingabegrössen beschäftigt. Stichwort ist hier O-Notation.

Bei der `random_str()`-Funktion ist die Frage warum die eine nichtssagende Zahl als Argument nimmt und die dann verwendet um ein Wörterbuch auszusuchen. An der Stelle hätte der Aufrufer auch gleich das Wörterbuch übergeben können aus dem ausgesucht werden soll. Man hätte sich eine unnötige Indirektion gespart und beim Aufruf würde nicht 1 oder 2 stehen, sondern ein Name, der zumindest potentiell, dem Leser mehr sagen kann als eine „magische” Zahl.

In der gezeigten `text_render()`-Funktion wird nicht über das Datei-Objekt iteriert. Direkt nach dem ``with`` werden die Zeilen einmal komplett in den Arbeitsspeicher in eine Liste geladen. Das auch noch nicht einmal mit `list()` oder der `readlines()`-Methode sondern in dem erst die ganze Datei als eine Zeichenkette eingelesen wird und dann daraus eine Liste mit Zeilen durch aufteilen an Zeilenende-Zeichen erstellt wird.

Die Funktion ist auch fehlerhaft. Im ``finally``-Zweig wird `textfile` niemals `True` sein. Wo sollte das denn passieren? Falls das passieren könnte, könnte allerdings `textfile` im ``except``-Zweig an die 0 gebunden worden sein. Dann würde hier versucht auf einer ganzen Zahl die `close()`-Methode aufzurufen ­— so eine Methode haben Zahlen aber nicht. Sinn macht das sowieso alles nicht, weil der ``with``-Block ja schon dafür sorgt, dass die Datei auf jeden Fall beim verlassen des Blocks geschlossen wird.

Die Idee mit dieser Datei ist auch an sich problematisch. Man gibt ja offenbar die Zeilennummern in der Datei an. Das ist an sich schon mal total unpraktisch, weil man Texte mit einer bestimmten Bedeutung im Programm dann mit zwei nichtssagenden Zahlen repräsentiert. Wenn man einen zusammengehörigen Zeilenblock ändert, so dass sich auch die Länge ändert, muss man das gesamte Programm durchgehen und alle Aufrufe anpassen die Zeilenblöcke nach dem Geänderten anpassen weil sich dort die Zeilennummern verändert haben. Dazu gibt man nicht wirklich die Zeilennummern an, die einem die meisten Texteditoren ja noch anzeigen können, sondern leere Zeilen werden dabei gar nicht mitgezählt. Wenn man also einen Text ausgeben lassen möchte, muss man im Texteditor nach der Zeilennummer schauen und dann die Lerrzeilen davor zählen und abziehenen. Umständlicher und damit auch fehleranfälliger geht's kaum. Oder man verwendet keine Leerzeilen. Dann kann man die Zeilenblöcke aber auch nicht gegeneinander abgrenzen um sich leichter in der Datei orientieren zu können.

Ein Grund warum die `load_sounds()`-Funktion so lang geworden ist dürfte sein, dass die viel zu viel macht. Neben dem laden der Sounds, was eigentlich ja nur ein Testen ist, denn man kann ja gar nicht mehrere Musiken laden, ist da noch Benutzerinteraktion und das Erstellen eines Fehlerberichts enthalten.

Dann stellt sich die Frage warum die Information welche Sounds geladen/getestet werden nicht als Argumente übergeben werden, so dass man die Funktion einfacher testen und wiederverwenden kann. Und warum sind jetzt plötzlich die zusammengehörigen Daten aus dem ursprünglichen Wörterbuch in zwei Listen aufgeteilt?

Als nächstes fällt auf das nicht nur die Eingabewerte aus Konstanten kommen, sondern die Funktion auch gar keinen tatsächlichen Rückgabewert hat. Die Wörterbücher die mit den Ergebnissen gefüllt werden sind global definiert und wie Konstanten benannt, obwohl sie im Programmverlauf geändert werden. Letztendlich ist das keine saubere Funktion sondern nur ein benannter Code-Abschnitt der auf globalen Daten arbeitet. Sie ist nicht unabhängig von globalen Daten und man kann sie zum Testen nicht einmal mehrfach hintereinander aufrufen ohne vorher manuell die Ergebnisse zu leeren.

Das iterieren über eine Sequenz mit Zahlen bloss um die dann als Index in Listen zu verwenden ist „unpythonisch” weil das ein unnötiger Umweg ist. Man kann *direkt* über die Elemente von Listen iterieren.

Und dann ist da schon wieder eine völlig sinnlose Schleife drin. Die zweite ``for``-Schleife im ``if response_snd == 1:``-Zweig wird genau einmal durchlaufen weil am Ende jedes Zweiges innerhalb der Schleife ein ``return`` ausgeführt wird.

Mal so als Idee wie man das Laden/Testen der Sound-Dateien von der Benutzerein- und -ausgabe trennen kann (ungetestet):

Code: Alles auswählen

import pygame
pygame.mixer.init()


SOUND_NAME_TO_FILENAME = {
    'noticed': 'signatur-Dil-7491_hifi.mp3',
    'bkgrnd_people': 'People_T-Doogens-8720_hifi.mp3',
    'win': 'Applause-SFXsourc-9027_hifi.mp3',
    'check': 'plim-Gato_pre-7790_hifi.mp3',
}


def load_sounds(sound_name2filename):
    result = dict()
    errors = list()
    for sound_name, filename in sound_name2filename.items():
        try:
            pygame.mixer.music.load(filename)
            result[sound_name] = filename
        except pygame.error:
            result[sound_name] = None
            errors.append((filename, pygame.error))
    return result, errors


def ask_user_for_sound(sound_name2filename):
    while True:
        response = input('SOUND aktivieren? [1] JA  |  [0] NEIN ')
        if response == '1':
            sounds, errors = load_sounds(sound_name2filename)
            if errors:
                print('Could not load the following sounds:')
                for filename, error in errors:
                    print('  {}: {}'.format(filename, error))
            return sounds
        elif response == '0':
            return dict.fromkeys(sound_name2filename, None)
        else:
            print('Falsche Eingabe.')
Antworten