Fortführung anderer Thread / zahl_erraten.py

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
BSA
User
Beiträge: 38
Registriert: Freitag 6. Dezember 2013, 07:49

Hallo Leute,

um den Bereich "Installation/Konfiguration" nicht unnötig zuzumüllen, dachte ich mir, splitten wir das ganze mal ein wenig auf.

Meine Fragen bzgl. des Debuggings etc. pp. stelle ich weiterhin dort, die verbesserte Version des kleinen Scripts jedoch stelle ich am besten hier ein.

Für alle die nicht wissen worum es grade geht: Der andere Thread

Wie auch in heute Vormittag gilt: Ich bin für Tipps dankbar und freue mich über konstruktive Kritik!

Also, hier dann erstmal der Code.
Danke für eure Rückmeldungen! :-)

Code: Alles auswählen

[list=1]
[*]#!/usr/bin/python3.3
[*]
[*]import random
[*]import time[/list]
[*]
[*]
[*]def ende():
[*]	print(player, ", das Programm wird beendet")
[*]	time.sleep(1)
[*]	raise SystemExit
[*]
[*]
[*]def main():
[*]	print("Hallo ", player, "!")
[*]	print("In diesem kleinen Spiel geht es darum, die gesuchte Zahl zu erraten.")
[*]	print("Du hast dafür 7 Versuche.")
[*]	print("Möglich sind alle ganzen Zahlen zwischen 1 und 100.")
[*]	print("***************************************************************")
[*]	print("***************************************************************")
[*]	prelude()
[*]
[*]
[*]def prelude():
[*]	while True:
[*]		interact = input(">> Drücke [1] + [enter] um fortzufahren. "
[*]		"Drücke [0] + [enter] um das Programm zu beenden.")
[*]		if interact == '0':
[*]			break
[*]		if interact == '1':
[*]			game()
[*]	ende()
[*]			
[*]def game():
[*]	while True:
[*]		starttime = time.time()
[*]		x = random.randint(1,100)
[*]		print("***************************************************************")
[*]#		print("<<<TESTING>>> Die gesuchte Zahl lautet: ", x)
[*]		print()
[*]		
[*]		for i in range(1,9):
[*]			eingabe = True
[*]			if i == 8:
[*]				print("-----------------------------------")
[*]				print(player, ", du hast es leider nicht geschafft.")
[*]				print("Versuche es am besten gleich nochmal!")
[*]				break
[*]
[*]			print("Versuch ", i)
[*]
[*]			while True:
[*]				try:
[*]					digit = int(input(">> Deine Zahl: "))
[*]					break
[*]				except:
[*]					print("Ungültige Eingabe. Bitte wiederhole deine Eingabe...")
[*]
[*]			if x is not digit:
[*]				if x > digit:
[*]					print("Die gesuchte Zahl ist größer als {0}".format(digit))
[*]				else:
[*]					print("Die gesuchte Zahl ist kleiner als {0}".format(digit))
[*]
[*]			else:
[*]				endtime = time.time()
[*]				time_used = endtime-starttime
[*]				print("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
[*]				print("+      Herzlichen Glückwunsch! Die gesuchte Zahl war {0}.      +".format(digit))
[*]				print("+      Benötigte Versuche: ", i, "                                +")
[*]				print("+      Deine Zeit: {0:.2f} Sekunden oder ({0:.2f}/60) Minuten.    +".format(time_used))
[*]				print("+      Ergebnis erzielt:", time.strftime("%d.%m.%Y %H:%M:%S                  +"))
[*]				print("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
[*]				break
[*]
[*]		print()
[*]		print()
[*]		print("***************************************************************")
[*]		print("***************************************************************")
[*]		print("***************************************************************")
[*]		break
[*]
[*]
[*]
[*]if __name__ == '__main__':
[*]	player = input(">> Wie ist dein Name? ")
[*]	while True:
[*]		main()[/list]


PS: Das mit der [_list_]-Funktion klappt wohl nicht, was mach ich falsch?
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: Was Du mit List falsch machst ist wohl das Du es überhaupt verwendest. Was sollte das denn werden?

Jetzt ist die Einrückung ein Leerzeichen zu wenig pro Ebene. :-)

``raise SystemExit`` ist ungewöhnlich. Dafür ist die `sys.exit()`-Funktion eigentlich da. Und letztendlich wäre es besser wenn man das ganze so organisiert, dass das überhaupt nicht notwendig ist und das Programm auf ”natürliche” Weise endet in dem es einen Programmablauf gibt der einfach das Ende des Moduls erreicht und damit das Ende des Programms. Schreibe Funktionen möglichst so dass der Aufruf auch zum Aufrufer zurück kehrt, denn das ist der Normalfall den man erwarten würde.

Auf Modulebene sollten keine Variablen definiert werden, nur Konstanten. `player` wird in den Funktionen benutzt und kommt dort einfach so aus dem „nichts”. Werte sollten Funktionen als Argumente betreten und Rückgabewerte verlassen.

Warum ein `time.sleep()` vor Programmende? Wenn das eine Konsolenanwendung ist, dann ist das einfach nur eine unnötige Pause bevor der Benutzer die Konsole wieder verwenden kann.

Ich weiss nicht ob `ende()` eine eigene Funktion sein sollte. Wenn man das unnötige warten und den Programmabbruch weg lässt, ist das ja nur noch eine Ausgabe einer einzelnen Zeile.

Die Verkettung der Funktionen ist auch unschön. Da spielt zum einen rein das Funktionen nach dem sie ihre Arbeit getan haben zurückkehren sollten, und zum anderen das die Namen nicht passen. `prelude` ist etwas was vor allem anderen passiert. Hier kommt es nicht nur nach `main` sondern *beinhaltet* auch noch das ganze Spiel denn wenn `prelude` zuende ist, ist auch `main` und `game` mindestens einmal komplett abgearbeitet worden. Semantisch wäre `prelude()` eher das was in `main()` passiert und man sollte lieber Funktionsaufrufe hintereinander schreiben die nacheinander ausgeführt werden sollen statt die Funktionen so zu verketten das eine die nächste aufruft. Das ist undurchsichtiger und es wird schwieriger etwas dazwischen zu setzen.

Vor der `main()` etwas wie `prelude()` auszuführen wäre aber auch ungewöhnlich. `main()` ist üblicherweise der Name der Funktion die als erstes ausgeführt wird.

Es ist auch schon wieder, oder immer noch eine ``while``-Schleife zu viel. Auf Modulebene ist eine Endlosschleife die eine Funktion mit einer Endlosschleife enthält die abgebrochen wird wenn der Spieler nicht mehr spielen möchte. Das ist *wieder* der Grund warum Du überhaupt so etwas wie `sys.exit()` benötigst, weil ohne das diese Endlosschleife auf Modulebene die `main()`-Funktion wieder aufruft die erst dann verlassen würde wenn der Spieler gesagt hat er will nicht mehr.

Und auch die Spielfunktion enthält eine total sinnlose äussere Endlosschleife. Die läuft genau *einmal* und wird dann mit einem ``break`` abgebrochen. Schleifen die grundsätzlich immer genau einmal laufen machen keinen Sinn.

`interact` ist eher ein Funktions- oder Methodenname als einer für die Benutzereingabe denn der Name beschreibt eine Tätigkeit.

`player` würde ich `player_name` nennen. Das mag jetzt noch nicht so wichtig sein, aber wenn objektorientierte Programmierung ins Spiel kommt, würde man hinter `player` eher ein komplexeres Spieler-Objekt statt einer einfachen Zeichenkette erwarten.

Die maximale Anzahl der Versuche sollte nicht so über das Programm verstreut sein. Wenn man das mal ändern möchte muss man das an drei Stellen tun, und dann auch noch jedes mal eine *andere* Zahl verwenden. Dafür sollte man einmal eine Konstante definieren und dann die anderen Werte relativ dazu berechnen. Wobei das in der Schleife die eine Iteration weiter geht um darin dann auf die Iteration nach dem letzten Versuch zu prüfen auch irgendwie etwas umständlich ist. Ich würde da lieber eine Schleife über die Versuche schreiben und dann dafür sorgen dass der Code der über das verlorene Spiel informiert nur dann erreicht wird, wenn die Schleife mit den Versuchen durchlaufen wurde ohne das die Zahl erraten wurde.

Die Grenzen des Ratespiels sind ebenfalls an *zwei* Stellen im Quelltext hart kodiert.

`eingabe` wird nirgends verwendet.

`digit` heisst auf Deutsch „Ziffer”, der Benutzer kann und sollte dort unter Umständen aber mehr als eine Ziffer eingeben können.

Du hast immer noch das ”nackte” ``except:`` statt dort anzugeben welche Ausnahmen Du dort erwartest wenn eine falsche Eingabe getätigt wurde.

Mit ``is not`` vergleicht man keine Werte. ``is`` und ``is not`` ist zum Vergleichen von Objektidentitäten. Für Werte verwendet man ``!=`` und ``==``. Dein Programm kann so funktionieren, muss es aber nicht:

Code: Alles auswählen

In [10]: a
Out[10]: 4711

In [11]: b
Out[11]: 4711

In [12]: a is b
Out[12]: False

In [13]: a is not b
Out[13]: True

In [14]: a == b
Out[14]: True
`a` und `b` sind zwar nicht an das selbe Objekt gebunden, die beiden Objekte haben aber trotzdem den gleichen Wert.

Idee für die Zukunft: Nur die Grenzen als Konstanten angeben und die Anzahl der Versuche daraus berechnen. Denn man kann ausrechnen wieviele Versuche man für eine Lösung mindestens benötigt um die Lösung auf jeden Fall zu finden. Kleine Denksportaufgabe. :-)

Ich komme dann als Zwischenstand ungefähr bei so etwas hier heraus (ungetestet):

Code: Alles auswählen

#!/usr/bin/python3.3
import random
import time

MIN, MAX = 1, 100
MAX_TRIAL_COUNTS = 7  # TODO Calculate this value from `MIN` and `MAX`.


def play_game(player_name):
    start_time = time.time()
    secret_number = random.randint(MIN, MAX)
    print('***************************************************************')
    print()
    for trial_count in range(1, MAX_TRIAL_COUNTS + 1):
        print('Versuch ', trial_count)

        while True:
            try:
                guessed_number = int(input('>> Deine Zahl: '))
                break
            except ValueError:
                print('Ungültige Eingabe. Bitte wiederhole deine Eingabe...')

        if secret_number != guessed_number:
            print(
                'Die gesuchte Zahl ist {0} als {1}'.format(
                    'größer' if secret_number > guessed_number else 'kleiner',
                    guessed_number
                )
            )
        else:
            time_used = time.time() - start_time
            print('+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')
            print('+      Herzlichen Glückwunsch! Die gesuchte Zahl war {0}.      +'.format(guessed_number))
            print('+      Benötigte Versuche: ', trial_count, "                                +")
            print('+      Deine Zeit: {0:.2f} Sekunden oder ({0:.2f}/60) Minuten.    +'.format(time_used))
            print('+      Ergebnis erzielt:', time.strftime("%d.%m.%Y %H:%M:%S                  +"))
            print("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
            break
    else:
        print('-----------------------------------')
        print(player_name, ', du hast es leider nicht geschafft.')
        print('Versuche es am besten gleich nochmal!')


def main():
    player_name = input('>> Wie ist dein Name? ')
    print('Hallo', player_name, '!')
    print(
        'In diesem kleinen Spiel geht es darum, die gesuchte Zahl zu'
        ' erraten.'
    )
    print('Du hast dafür {} Versuche.'.format(MAX_TRIAL_COUNTS))
    print(
        'Möglich sind alle ganzen Zahlen zwischen {} und {}.'.format(MIN, MAX)
    )
    print('***************************************************************')
    print('***************************************************************')
    while True:
        response = input(
            '>> Drücke [1] + [enter] um fortzufahren.'
            ' Drücke [0] + [enter] um das Programm zu beenden.'
        )
        if response == '0':
            break
        elif response == '1':
            play_game(player_name)
            print()
            print()
            print('***************************************************************')
            print('***************************************************************')
            print('***************************************************************')

    print(player_name, ', das Programm wird beendet.')


if __name__ == '__main__':
    main()
BSA
User
Beiträge: 38
Registriert: Freitag 6. Dezember 2013, 07:49

Den Code hab ich ausprobiert - funktioniert, soweit ich das beurteilen kann, einwandfrei.

Ich hab noch eine Menge zu lernen, und da ist es - für mich - sehr gut, über solche "Korrekturen" meinen Stil besser anzupassen.

Ich muss jedoch gestehen, ich hab mich mit dem Code bisher nur zweimal kurz auseinandergesetzt und ihn auch noch nicht selber (nach Deiner Vorlage) eingetippt, um das erhaltene auch selber anzuwenden.

Ich hänge mom. noch an dem Algorythmus bzw. dem kleinen Script (später: der Funktion innerhalb des Hauptprogramms bzw. zu importierendem Modul, s.w.u.) zur automatischen Berechnung der mathematisch maximal benötigten Schritte, um die Anzahl der Schritte (=Versuche im Spiel) zu ermitteln.

Die Logik ist mir beim Spielen meines kleinen Ratespiels selber aufgegangen:

Man teile einmal durch 2
- ist x < y, teile weiter bis x > y
- ist x > y, erhöhe y um die hälfte

Bsp.:
x = 5

Rechenweg:
1___________100 / 2 = 50 | x < y
2___________050 / 2 = 25 | x < y
3___________025 / 2 = 13 | x < y
4___________013 / 2 = 07 | x < y
5___________007 / 2 = 04 | x > y
6_________y + 0.5y = 06 | x < y_1
7___(y_1 - y) / 2 + y = 5 | x = y_2

Mit maximal 7 Versuchen kann man also jede x-beliebige (ganze) Zahl in einem Bereich von 1 - 100 mit der gegebenen Hilfestellung herausbekommen. (deshalb ist mein Programm auf 7 eingestellt, und nicht wie ganz zu Beginn auf 10)

Durch manuelles ausprobieren (im Programm) habe ich herausbekommen, dass bei einer range von 1 - 200 lediglich ein weiterer Versuch benötigt wird, um das Ergebnis nach dieser Methode mit hundertprozentiger Sicherheit zu erraten.

Mein Gedanke ist nun, einerseits, weil ich Lust darauf habe, andererseits aber auch, weil ich so denke ich ne recht gute Übung bekomme, ein Spiel zu erstellen, indem der Benutzer

1. Die Anzahl der Aufgaben auswählen kann,
2. die Range wählen kann und
3. eine von drei Schwierigkeitsstufen auswählen kann.

Das mit der Anzahl der Aufgaben sollte sich meinerseits leicht einbauen lassen. Da mache ich mir jetzt keine Gedanken drüber.
Um die Range jedoch frei wählbar zu gestalten, benötige ich einen Automatismus (s. Dein ToDo @BlackJack), welcher die maximal benötigte Anzahl mathematischer Schritte berechnen kann, damit das Ergebnis, ohne den Faktor Glück, ermittelt werden kann.
Dieser Wert (bei MIN,MAX=1,100 also 7) wäre dann für die 1. Schwierigkeitsstufe "Ich pack das!" gegeben.
Für die zweite Schwierigkeitsstufe "Glück gehabt" wäre natürlich ein Versuch abzuziehen.
Und für die dritte Schwierigkeitsstufe "Kopfzerbrechen" ein weiterer.


Das nächste große Problem DANN wäre für mich, eine Formel zu erstellen, die anhand der erreichten Treffer, z.B. 10 von 10 = 100% und der benötigten Zeit multipliziert mit einem Multiplikator "Schwierigkeit" einen Score ausgeben kann, der bei allen erdenklichen Konstellationen fair und nachvollziehbar bleibt.
Damit möchte ich mich jetzt aber definitiv noch nicht beschäftigen.

Mein Hauptaugenmerk seit einigen Tagen liegt darauf, diesen Algorythmus bzw. das Rechenschritte-Script (später die Funktion im Hauptprogramm bzw. danach evtl. das Modul) zu erstellen.

Meine Frage hierzu wäre erstmal, ob ihr ein Modul kennt, welches schon existiert, das diese Berechnungen ausführt (würde mir die Arbeit erleichtern...) ?
Im math-Modul habe ich nix passendes gefunden.


Ach ja, und zu meiner Verteidigung habe ich lediglich vorzubringen, dass ich meinen Schulabschluß vor über 12 Jahren erlangte und seitdem mit Mathe und Stochastik oder sonstigen Geschichten in dem Bereich nix mehr zu tun hatte :mrgreen:
Ich muss mir also alles Stück für Stück aneignen.
Eine Formel ist mir nicht bekannt.
Ich gehe momentan den Weg über if-elif-else.
Ich bin aktuell soweit, dass ich einen Teil der Zahlen mit der geringsten Anzahl der Schritte ausrechnen kann (vorallem kleine Zahlen).
Ich stehe noch vor dem Problem, dass ganze auf größere Zahlen zu übertragen...
Ich versuche den Code so klein wie möglich zu halten.
Bisher läuft nur eine äußere Schleife. Keine weitere.
Von 1 - 30 wurden vorgestern 14 Zahlen korrekt und auf dem kürzesten Weg ermittelt.


So. Jetzt wisst ihr auch, wieso ich gerne die Zeilen im Programmcode ausgeben können würde. Aktuell ist es nämlich so, dass ich, wenn ich 5 Rechenwege hinbekomme, und es am sechsten hapert, nicht weiß, nach welcher der aktuell 143 Zeilen in der while-Schleife noch Nachbesserungsbedarf besteht.

Ich werde den aktuellen Stand des Codes die Tage online stellen. Jeden zweiten Tag gehe ich eigentlich davon aus, dass es jetzt funktionieren könnte, nur um dann festzustellen, dass der Code wohl noch nicht lang genug ist...
Oder ich übersehe die Lösung vor lauter Lösungswegen... ??


Wenn er fertig ist - und falls er jemals funktionieren sollte -, werde ich ein Modul daraus machen, und öffentlich hier wie auf github zur Verfügung stellen, so dass jeder diesen Code bedingungslos (wie jedes normale Modul unter python) in seinen eigenen Codes für alle erdenklichen Verwendungen nutzen kann.


Aber bleiben wir mal auf dem Boden der Tatsachen. *ironie=True*Aktuell fliegt nämlich gleich als nächstes doch erst wieder die Tastatur gegen die Wand...*ironie=False*
;-)


Grüße
BSA
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: Einen Algorithmus brauch man zur Bestimmung der maximal nötigen Versuche nicht schreiben, das kann man direkt mit zwei Funktionen aus dem `math`-Modul ausrechnen. Das Verfahren zum finden der Zahl nennt sich übrigens Bisektion.

Edit: Nur so am Rande: Wenn man das Bisektionsverfahren mal braucht um etwas in einer sortierten Sequenz zu finden, braucht man es nicht selber implementieren denn in der Standardbibliothek gibt es das `bisect`-Modul. :-)
BSA
User
Beiträge: 38
Registriert: Freitag 6. Dezember 2013, 07:49

Och Mensch, und ich zermater mir hier seit Tagen den Kopf... :lol:

Ich fuchs mich da heut Abend mal ein.

Ach so, der code hat gestern Abend bereits für alle natürlichen Zahlen von 1 bis einschl. 21 den kürzesten Weg gefunden... :lol:

Sollte es irgendwen interessieren, kann ich den gerne mal posten. Sind ja bisher lediglich ~140 Zeilen auszuführender und etwa ebensoviele Zeilen auskommentierter code...

Bisektion also...math + bisect = Lebensrettung in letzter Sekunde ?? :K


DANKE für den Tipp!
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: Naja, `bisect` brauchst Du hier nicht, das habe ich nur so als Info am Rande erwähnt, dass man das in Python nicht selber Programmieren muss, *wenn* man es denn mal braucht. :-)
BSA
User
Beiträge: 38
Registriert: Freitag 6. Dezember 2013, 07:49

Also, da es mir schwerer schien, mich entweder in das unbenötigte Modul 'bisect()' einzuarbeiten oder ohne Kenntnis der Materie rauszufinden, welche beiden Funktionen aus dem math-Modul es denn nun in welcher Kombination sein sollen, habe ich heute Nacht um 01:45 Uhr meine eigene Funktion fertiggestellt und getestet gehabt.

Vielen Dank für den Fingerzeig mit den Wegen, da hat es bei mir nämlich gehapert! ;-)
Besonders der wiki-Link (Bisektion) war etwas handfestes, bei dem ich mich einarbeiten konnte.

Ich habe die Funktion zwar fertiggestellt, werde sie jedoch frühestens morgen Abend hier online stellen.

Ich möchte da nochmal vorher drüber schauen und werde es dann hier online stellen. Ich bitte aber schon jetzt um Nachsicht, da ich ja noch Anfänger bin :-)

Fakt ist: Es funktioniert! Und zwar mit allen natürlichen Zahlen! (andere, wie auch Zahlen >1000 habe ich aufgrund Zeitmangels noch nicht ausprobiert)

Der Code ist samt Beschreibung und "testing-Ausgaben" ('print'- und time.sleep()-Anweisungen zum Verfolgen der Rechenfehler) aktuell 92 Zeilen lang. Allerdings sind da - aufgrund der besseren Übersichtlichkeit für mich - noch eine Menge Leerzeilen mit drin. Eventuell werden auch einige Objekte (Variablen?) sowie ihre Formeln nicht benötigt, dass weiß ich alles noch nicht...
Außerdem stört mich noch, dass ich das Ding nicht in einer Endlosschleife ablaufen lassen kann. Nach dem ersten Ergebnis ist Schluß. Das Programm bleibt stehen. Da ist also noch etwas grobe Feinarbeit angesagt...

Ich möchte das ganze dann wie gesagt als Modul gestalten, nutzen und zur Verfügung stellen.
Das ganze ist ein sehr einfacher Weg für jeden, der das Bisektions-Verfahren einfach und ohne große Umstände bzw. Einarbeitung in ein anderes Modul benötigt. Dazu brauche ich dann aber noch die Möglichkeit, die range()-Variablen (MIN,MAX) mit Aufruf der (einzigen...) Methode(?) des dann fertigen Moduls übergeben zu können...

Naja, langer Rede kurzer Sinn. Ich hab nun Feierabend, muß gleich wieder aufstehen und bin müde. Ich meld mich wieder!

BSA


PS: Das ganze hat natürlich ein wenig Zeit & Nerven gekostet, aber dafür auch Spaß gemacht, als es dann fertig war und funktionierte natürlich erst recht! Den größten Batzen Zeit hab ich verloren, weil ich anfangs einfach nur nach Gefühl vorgegangen bin. Es hat sich zwar gezeigt, dass der Grundgedanken richtig war, es aber in der Umsetzung enorme Mängel gab. Erst als ich was greifbares (Bisektion) hatte und meine Gedanken kurz zu Papier gebracht habe, ging es dann doch recht fix vorwärts :-)
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
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

BSA hat geschrieben: ('print'- und time.sleep()-Anweisungen zum Verfolgen der Rechenfehler)
Hui... ``time.sleep`` zum Verfolgen von Rechenfehlern... sitzt Du vor der Ausgabe und willst "live" mitrechnen? :mrgreen:
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

BSA hat geschrieben:Also, da es mir schwerer schien, mich entweder in das unbenötigte Modul 'bisect()' einzuarbeiten oder ohne Kenntnis der Materie rauszufinden, welche beiden Funktionen aus dem math-Modul es denn nun in welcher Kombination sein sollen, habe ich heute Nacht um 01:45 Uhr meine eigene Funktion fertiggestellt und getestet gehabt.
``math.log`` und ``math.ceil``. Hast du dir zur Bestimmung der Anzahl keine Gleichung aufgestellt? Dann wären dir die benötigten Funktionen sofort aufgefallen.
BSA hat geschrieben:Ich möchte das ganze dann wie gesagt als Modul gestalten, nutzen und zur Verfügung stellen. Das ganze ist ein sehr einfacher Weg für jeden, der das Bisektions-Verfahren einfach und ohne große Umstände bzw. Einarbeitung in ein anderes Modul benötigt.
Du müsstest dich nicht in dein Modul einarbeiten, jeder andere schon ;-)
BSA hat geschrieben:Dazu brauche ich dann aber noch die Möglichkeit, die range()-Variablen (MIN,MAX) mit Aufruf der (einzigen...) Methode(?) des dann fertigen Moduls übergeben zu können...
Das geht ganz einfach: verwende Funktionen und arbeite nicht mehr auf Modulebene. Dann kannst du die Werte als Parameter übergeben.
BSA hat geschrieben:Den größten Batzen Zeit hab ich verloren, weil ich anfangs einfach nur nach Gefühl vorgegangen bin. Es hat sich zwar gezeigt, dass der Grundgedanken richtig war, es aber in der Umsetzung enorme Mängel gab. Erst als ich was greifbares (Bisektion) hatte und meine Gedanken kurz zu Papier gebracht habe, ging es dann doch recht fix vorwärts :-)
Das wird mit der Zeit besser. Viele Dinge lassen sich irgendwann sehr intuitiv lösen und komplexere Sachverhalte wirst du schneller erkennen.
Das Leben ist wie ein Tennisball.
BlackJack

@BSA: Die `log`-Funktion steht doch im Wikiartikel, nur auf das Aufrunden muss man noch selber kommen. Also als Formel ⌈log₂ n⌉ mit n = Anzahl der Zahlen.
BSA
User
Beiträge: 38
Registriert: Freitag 6. Dezember 2013, 07:49

Moin Leute,

es hat sich zwar etwas gezogen, aber das ist eine andere Geschichte...
Da ich Probleme mit meinem LMDE hatte, habe ich zwischenzeitlich auf Mint16, gleiche Probleme und dann Mint13 (Maya) gewechselt. Da ich damit dann aber auch nicht zufrieden war, und 46x Updates seit Erscheinen mir auch nicht "zuviel" vorkamen, bin ich mittlerweile wieder auf LMDE umgestiegen.
Einen font-config-Fehler (Monospace) habe ich zwar immernoch, aber mit dem komme ich zurecht.
Mit dem "Umsteigen" war das auch nicht immer soo einfach...jedes mal musste ich die Partitionen nach Windoof (ab SDA4 - primäre extended Partition mit / , swap & /home per GPartedLive wieder runterhauen usw usf...DAS! war mal eine for-Schleife :mrgreen:
Naja, jetzt läufts soweit.
Das Problem mit der Nicht-Ausführbarkeit von python-Scripts habe ich mittlerweile auch gelöst, weiß leider nicht mehr wie - eher durch Zufall als durch Wissen. Lag aber glaub ich entweder an geany oder an einem fehlerhaften Ausführbar-machen durch mich, wobei mir das immernoch nicht erklärt, wieso das Terminal dann env nicht finden konnte, bzw. im Script nicht. env selber hat im Terminal einwandfrei funktioniert...bzw tut es jetzt zumindest. Das ist jetzt aber ne andere Baustelle...


Zum Thema zurück.
Ich werde hier heute die Funktion zur Ermittlung der maximal nötigen Schritte (f_bisektion - by BSA :-) ) veröffentlichen.
Ich musste sie für mein Spiel jedoch leicht modifizieren, da ich für mein Spiel eine andere Angabe aus der Funktion brauchte, was ihr aber selber anhand der beiden Codes ablesen könnt.
Für die Ermittlung der maximal nötigen Schritte musste ich (für mich...) erstmal eine Funktion entwickeln, die zuverlässig die richtigen Rechenschritte ausübt, ohne dabei zu viele Fehler zu produzieren.
Für mein Spiel brauchte ich jedoch eine Funktion, die mir am Ende auch den entsprechenden Wert der benötigten Schritte für eine entsprechend zu übergebende Variable lieferte, oder anders gesagt:
ich wollte die Funktion nochmal speziell auf das Spiel anpassen.

Vorneweg sage ich, dass beide nicht hundert Prozent fehlerfrei arbeiten.
Es gibt vereinzelt Zahlen, bei denen der Rechner rechnet und auf kein Ergebnis kommt. Diese sind jedoch verschwindend gering und wenn ich mal etwas mehr Zeit über habe, werde ich das auch noch in Angriff nehmen.
Ich denke ich weiß sogar schon, woran genau es liegt. Die Ergebnisse der Einzelschritte werden ständig auf INT degradiert, was aller Wahrscheinlichkeit nach zu eben jenem Phänomen führt.

Das ist für mich aber momentan nicht so wichtig, da ich das Spiel etwas umgemodelt habe...man könnte auch sagen, ich hab es angepasst. Ein HighScore ist noch nicht implementiert, das kommt dann aber auch noch irgendwann und zwar als Übung, da ich die Datei "highscore" auslagern werde, und zwar einmal in eine .txt, einmal in eine .csv und wenn es mir die Zeit erlaubt evtl. auch noch mal in eine .bin - aber letzteres hat auf jeden Fall erstmal noch Zeit...

Wie gesagt, das Spiel ist leicht modifiziert und eigentlich auch noch lange nicht fertig...

Z.B. lässt es sich jetzt aktuell nicht mehr sauber beenden, ich habe dafür einen Fehler einbauen müssen, durch den es aber erstmal abbricht.
Außer dem sind noch etliche andere Sachen zu machen, etwa Verbesserungen am Code, am Style, an den Funktionen und am gesamt-Umfeld, die da wären:
- Sound (per pygame - müsste ich mir erst noch kompilieren oder warten bis es für 3.3 released wird)
- GUI (evtl. per tkinter oder Qt oder sonst irgendwas womit sich umgehen lässt...hier kein pygame)
- und noch so einige andere Sachen, die mir im Kopf rumschwirren :mrgreen:

Die Codes stelle ich dann am Ende ein.

@Hyperion
Hui... ``time.sleep`` zum Verfolgen von Rechenfehlern... sitzt Du vor der Ausgabe und willst "live" mitrechnen?
Jupp, ziemlich genau so lief das da ab :mrgreen:
Wenn ich dir die div. Stationen meiner (vor-)Funktions-Findung zeigen würde, würdest du bestimmt mit den Ohren schlackern :-D

@EyDu
``math.log`` und ``math.ceil``. Hast du dir zur Bestimmung der Anzahl keine Gleichung aufgestellt? Dann wären dir die benötigten Funktionen sofort aufgefallen.
Hm...mit denen hab ich mich noch gar nicht befasst, könnt ich aber nochmal tun!
Nee, bzw. so halbwegs. Anfangs hatte ich soviele Ideen die alle nur bei XY-Prozent geklappt haben, und kein mathematisch greifbares Verfahren zur Hand, dass ich mir meine Zeit recht zwiespältig (Frust&Freude) vertrieben hab.
Nachdem ich dann den Tipp mit dem "Bisektion"s-Verfahren von BlackJack bekommen hab, ging es dann aber doch recht fix von der Hand - weniger als einen Tag hat es gedauert, bis die erste Funktion fertig war und lief.
Danach war erstmal stillstand in Sachen python...
Du müsstest dich nicht in dein Modul einarbeiten, jeder andere schon
Nee, mag sein, dass es nicht allzu funktionsreich sein sollte, aber es ist echt einfach zu benutzen.
Siehst du nachher im Code, aber auch im Spiel.
Als Modul hab ich das ganze bisher noch nicht gemacht, kommt aber vielleicht auch noch, wenn es dann fehlerfrei läuft.
Aktuell ruft man die Funktion im game folgendermaßen auf

Code: Alles auswählen

f_bisect(MIN,MAX)
Wobei MIN eigentlich rausfallen könnte, da ich gemerkt habe, dass ein Bereich von Bspw. 100 - 200 wie der Bereich von 1 - 200 behandelt wird, was zur Folge hätte, das man einen Versuch zuviel zugestanden bekommen hätte.
Das ist aber nur noch Feinjustierung die ich beizeiten nachholen werde. Aktuell habe ich das Thema anders gelöst, siehst du dann im Spiel-Code :-) (der untere Bereich ist fest -> globale Variable MIN = 1 -> keine Auswahl mehr von MIN durch den Anwender)

Das wird mit der Zeit besser. Viele Dinge lassen sich irgendwann sehr intuitiv lösen und komplexere Sachverhalte wirst du schneller erkennen.
Wollen wir's hoffen :-)


@BlackJack
@BSA: Die `log`-Funktion steht doch im Wikiartikel, nur auf das Aufrunden muss man noch selber kommen. Also als Formel ⌈log₂ n⌉ mit n = Anzahl der Zahlen.
Ja, ich hab mir das angeschaut und es kam mir irgendwie zu komplex rüber, aber ich werde, allein schon um es mal gemacht zu haben, versuchen, eine kleine entsprechende Funktion per "math.log" und "math.ceil" zu schreiben. Bisher muss ich gestehen, hab ich mich mit den Dokumenten auf python.org und in den Wikis noch nicht wirklich viel beschäftigt - das kommt (muss ja) aber auch noch! :-)
Außerdem hatte ich ganz zu Beginn schon angefangen, noch bevor mir Bisektion überhaupt ein Begriff war, das ganze über if/else-Anweisungen zu schaffen...das gab aber nur elendig lange Verschachtelungen, null Struktur und keine Übersichtlichkeit - *ironie=1* die Funktionalität nahm dafür proportional zum geschriebenen Code zu :lol: *ironie=0*


So. Nun ist aber Zeit für die Codes.
Und schlafen muss ich auch noch schnell, kurz & bündig...


Als erstes kommt der leicht überarbeitete Code der Funktion f_bisektion, im Grunde sind nur unnötige Variablen rausgeflogen, die keine erkennbare Funktion hatten (im Spiel ursprünglich: f_bisect, aktuell selbst nicht mehr implementiert).

Code: Alles auswählen

#!/usr/bin/env python3

########################################
# Diese Funktion zum Modul umarbeiten. #
#    BSA | 13.12.2013 | 01:45 Uhr      #
#     überarbeitet am 18.11.2013       #
########################################

import random
import time


def main():
	# MIN,MAX durch Eingabe des Nutzers definieren _ hier testing
	MIN,MAX = 1,1000

	x = random.randint(MIN,MAX)
#	x = 512							# Testing
	print("x   = ", x)				# Testing
	obergrenze = MAX
	untergrenze = MIN
	trial_count = 1
	y = obergrenze / 2				# erste Lokalisierung, Ausgabe für
	print('Versuch: ', trial_count, 'y = ', y)		# testing
	seek = True
	
	while True:
		if y == x:
			print("MATCH! x = ", y)				# testing
			print("COUNTER: ", trial_count)		# testing
			time.sleep(1)						# testing
			seek = False
			break

		if x < y:
			obergrenze = int(y)
			y = int(((obergrenze - untergrenze) / 2) + untergrenze)

			l = range(0,y)
			l_len = int(len(l))
			l_mw = len(l) / 2
			r = range(y,obergrenze)
			r_len = int(len(r))
			r_mw = r_len / 2

			trial_count = trial_count+1
			# Ausgabe für testing
			print("Versuch: ", trial_count, "55 | y   = ", y)			
			seek = True

		if x > y:
			untergrenze = int(y)
			y = int(untergrenze + ((obergrenze - untergrenze) / 2))
			
			l = range(untergrenze,obergrenze + (obergrenze - untergrenze))
			l_len = int(len(l))
			l_mw = len(l) / 2
			r = range(untergrenze,obergrenze)
			r_len = int(len(r))
			r_mw = len(r) / 2
			
			trial_count = trial_count+1
			# Ausgabe für testing
			print("Versuch: ", trial_count, "75 | y   = ", y)
			seek = True


if __name__ == '__main__':
	main()
Wie ihr seht, hab ich wirklich für's Testing Ausgaben erzeugt, sogar mit ungefährer Zeilenangabe (später verrutscht), um zu sehen, wo es zeitweise noch hakte bzw. haken könnte.
Nachdem der Code funktionierte, habe ich die "time.sleep()"-Anweisungen rausgenommen, da war dann nur noch das Endergebnis interessant.
_____________________________________________________________________


Hier dann noch der etwas abgewandelte und besser auf das Spiel abgestimmte Code.

Code: Alles auswählen

#!/usr/bin/env python3

########################################
# Diese Funktion zum Modul umarbeiten. #
#    BSA | 13.12.2013 | 01:45 Uhr      #
#     überarbeitet am 18.11.2013       #
########################################

import random
import time


def main():
	# MIN,MAX durch Eingabe des Nutzers definieren _ hier testing
	MIN,MAX = 198,200													# falsche Ausgabe bei höherem Einstieg durch MIN

	for i in range(MIN,MAX):
		x = i
		obergrenze = MAX
		untergrenze = MIN
		trial_count = 1
		y = obergrenze / 2												# !!! trotz y = ((obergrenze - untergrenze) / 2) + untergrenze falsches Ergebnis... ?

		while True:
			if y == x:
				print("MATCH! x = ", y)
				print("COUNTER: ", trial_count)
				break
			else:
				if x < y:
					obergrenze = int(y)
					y = int(((obergrenze - untergrenze) / 2) + untergrenze)
					l = range(0,y)
					l_len = int(len(l))
					l_mw = len(l) / 2
					r = range(y,obergrenze)
					r_len = int(len(r))
					r_mw = r_len / 2
					trial_count = trial_count+1		
				elif x > y:
					untergrenze = int(y)
					y = int(untergrenze + ((obergrenze - untergrenze) / 2))					
					l = range(untergrenze,obergrenze + (obergrenze - untergrenze))
					l_len = int(len(l))
					l_mw = len(l) / 2
					r = range(untergrenze,obergrenze)
					r_len = int(len(r))
					r_mw = len(r) / 2
					
					trial_count = trial_count+1
				else:
					continue
	trials_needed = trial_count + trial_count							# + t_c nötig ?
	trials_max = int(trials_needed / 2)
#	print('Höchste Anzahl benötigter Versuche: ', trials_max)

if __name__ == '__main__':
	main()
___________________________________________________________________________



So. Und zu guter letzt natürlich noch das Spiel. Zum Spiel weiter unten vielleicht noch ein paar Kleinigkeiten.
Aber erstmal der Code...

Code: Alles auswählen

#!/usr/bin/env python3
import random
import time
import sys

MIN = 1
# Divisionsoperator range / Liste f.range-Begrenzungsregel MAX (STEP_50)
div_op_range = []
# 50 / 1 = 1, 100 / 2 = 2 ... 2000 / 50 = 40
for i in range(1,41):
	div_op_range.insert(i,i)


def f_bisection(player_name,MIN,MAX):
	for i in range(MIN,MAX):
		x = i
		obergrenze = MAX
		untergrenze = MIN
		trial_count = 1
		y = obergrenze / 2

		while True:
			if y == x:
				break
			else:
				if x < y:
					obergrenze = int(y)
					y = int(((obergrenze - untergrenze) / 2) + untergrenze)
					l = range(0,y)
					l_len = int(len(l))
					l_mw = len(l) / 2
					r = range(y,obergrenze)
					r_len = int(len(r))
					r_mw = r_len / 2
					trial_count = trial_count+1		
				elif x > y:
					untergrenze = int(y)
					y = int(untergrenze + ((obergrenze - untergrenze) / 2))					
					l = range(untergrenze,obergrenze + (obergrenze - untergrenze))
					l_len = int(len(l))
					l_mw = len(l) / 2
					r = range(untergrenze,obergrenze)
					r_len = int(len(r))
					r_mw = len(r) / 2
					
					trial_count = trial_count+1
				else:
					continue
	trials_needed = trial_count + trial_count
	trials_max = int(trials_needed / 2)
	set_difficulty(player_name,MIN,MAX,trials_max)


def main():
	print('Willkommen bei _guess_!')
	time.sleep(1)
	print('In diesem kleinen Spiel geht es darum...')
	time.sleep(0.5)
	print('die gesuchte Zahl zu finden.')
	time.sleep(0.5)
	print('Du bekommst Hinweise darauf ob die gesuchte Zahl...')
	time.sleep(0.5)
	print('größer oder kleiner ist als die Zahl...')
	time.sleep(0.5)
	print('die du eingetippt hast.')
	time.sleep(0.5)
	print('Viel Spaß!\n\
	Und natürlich auch ein wenig Glück...')
	player_name = input('>> Wie ist dein Name? ')
	if player_name == 'TEST':								# testing
		set_range(player_name)								#
	print('Hallo', player_name, '!')
	print('********************GUESS********************')
	time.sleep(2)
	print('...')
	time.sleep(0.5)
	print('Es war einmal, vor langer Zeit...')
	time.sleep(1.5)
	print('eine Zahl, versteckt seit langer Zeit...')
	time.sleep(1.5)
	print('sie trollte allein so vor sich hin...')
	time.sleep(1.5)
	print('da sah sie auf einmal eine Tafel und ein Kind...')
	time.sleep(1.5)
	print('die Tafel war so voll mit tollen Zahlen...')
	time.sleep(3)
	print('da musste sie und rollte sofort hin.')
	time.sleep(1.5)
	print('Und an der Tafel angekommen, sah sie viele von sich...')
	time.sleep(1.5)
	print('und empfand eine Freude, ganz neu für sich...')
	time.sleep(1.5)
	print('und sie dachte sich, doch verriet natürlich nicht: "Hey! ')
	time.sleep(1.5)
	print('Wer mich findet, mit dem spiele ich!" ')
	time.sleep(5)
	print('Die Zeit verging und es ward´ dunkel...')
	time.sleep(1.5)
	print('Da sammelten sich viele nette Leute, ja man munkelt...')
	time.sleep(1.5)
	print('vor dieser riesen Tafel standen sie und dachten sich...')
	time.sleep(1.5)
	print('"Mensch, die ist aber gut versteckt! Na, die find´ doch ich!'
		'"')
	time.sleep(3)
	print('... und so begann vor langer Zeit dieses Spiel ...')
	time.sleep(4)
	print('... und {0}, denke daran, du bist nun mitten drin !'\
	.format(player_name))
	time.sleep(4)
	print('*********************************************')
	print('******************* GUESS *******************')
	print('*********************************************')
	time.sleep(4)
	print('')
	print('*****************EINLEITUNG******************')
	time.sleep(2)
	print('In diesem kleinen Spiel geht es darum...')
	time.sleep(1.5)
	print('die gesuchte Zahl zu finden.')
	time.sleep(1.5)
	print('Abhängig vom festgelegten Zahlenbereich...')
	time.sleep(1.5)
	print('und abhängig vom gewählten Schwierigkeitsgrad...')
	time.sleep(1.5)
	print('stehen dir eine bestimmte Anzahl an Versuchen frei...')
	time.sleep(1.5)
	print('um "Guessy" zu finden, die sich versteckt hält.')
	time.sleep(2)
	print('"Guessy" ist die Zahl, mit der du spielen willst...')
	time.sleep(1.5)
	print('du kannst den Zahlenbereich selber festlegen...')
	time.sleep(1.5)
	print('das heißt, du kannst zwischen 1 und 50, 1 und 100...')
	time.sleep(1.5)
	print('oder aber auch zwischen 1 und 2000 nach ihr suchen.')
	time.sleep(1.5)
	print('Die Anzahl deiner Versuche passt sich entsprechend an...')
	time.sleep(1.5)
	print('doch sei gewarnt...\n\
	hier wirkt sich auch der Schwierigkeitsgrad aus!')
	time.sleep(3)
	print('...')
	time.sleep(5)
	print('Bereit? Dann kommst du jetzt ins Konfigurationsmenü ...')
	set_range(player_name)


def set_range(player_name):
	print('')
	print('**************EINSTELLUNGSMENÜ***************')
	time.sleep(2)
	print('Du kannst den Zahlenbereich selbst auswählen.')
	time.sleep(0.5)
	print('Für den Anfang empfehle ich dir den Bereich von 1 - 100.')
	time.sleep(0.5)
	print('Du kannst aber natürlich auch im Bereich bis 10 anfangen...')
	time.sleep(0.5)
	print('und den Bereich im Konfigurationsmenü schrittweise'
		' erhöhen.')
	time.sleep(0.5)
	print('Du kannst den Zahlenbereich nach jedem Durchlauf...')
	time.sleep(0.5)
	print('in 50er Schritten ändern, nach oben wie nach unten.')
	time.sleep(0.5)
	print('Du kannst zum Beispiel im Bereich von 1 - 50, 1 - 100...')
	time.sleep(0.5)
	print('aber auch im Bereich von maximal 1 - 2000 suchen.')
	time.sleep(0.5)
	print('Die größte Zahl ist aus Gründen der Kompatibilität 2000!')
	while True:
		try:
			sett_max = int(input('>> Wähle jetzt die größte Zahl'
				' deines Suchbereichs: '))
			if sett_max >= 50 and sett_max <= 2000:
				MAX = sett_max
				div_op = float(MAX / 50)
				if div_op in div_op_range:
					break
				else:
					print('Denke bitte daran, nur in 50er'
						' Schritten vorzugehen.')
			else:
				if sett_max < 50:
					print('Bitte wähle einen etwas größeren Bereich...')
				elif sett_max > 2000:
					print('Denke daran: Die größte Zahl ist 2000 !')
		except ValueError:
			print('Gib ruhig eine vernünftige, also natürliche Zahl'
				' ein...')
	f_bisection(player_name,MIN,MAX)
	
	
def set_difficulty(player_name,MIN,MAX,trials_max):
	print('Also {}, es gibt drei'
		'Schwierigkeitsgrade.'.format(player_name))
	time.sleep(0.5)
	print('Fange am besten klein an, und steigere dich mit der Zeit.')
	time.sleep(0.5)
	print('Für die leichteste Stufe, bestätige die [1]')
	time.sleep(0.5)
	print('Für einen etwas höheren Nervenkitzel, bestätige die [2]')
	time.sleep(0.5)
	print('Und für ein wenig Kopfzerbrechen bestätige die [3]...')
	time.sleep(0.5)
	print('Doch denke daran: Je erhöhter Schwierigkeitsstufe\n\
	gibst du einen möglichen Versuch ab !')
	while True:
		difficulty = input('>> Wähle eine Schwierigkeitsstufe aus: ')
		if difficulty == '1':
			MAX_TRIAL_COUNTS = trials_max
			break
		elif difficulty == '2':
			MAX_TRIAL_COUNTS = trials_max - 1
			break
		elif difficulty == '3':
			print('PREPARING THRILLMODE...')
			time.sleep(0.5)
			print('ENTERING THRILLMODE...')
			time.sleep(0.5)
			print('YOU HAVE ENTERED THRILLMODE !')
			time.sleep(2)
			MAX_TRIAL_COUNTS = trials_max - 2
			break
		else:
			print('Bitte treffe eine gültige Auswahl...')
		break
	play_game(player_name,MIN,MAX,MAX_TRIAL_COUNTS)


def play_game(player_name,MIN,MAX,MAX_TRIAL_COUNTS):
	while True:
		start_time = time.time()
		secret_number = random.randint(MIN, MAX)
		print('*********************************************')
		print('******************* GUESS *******************')
		print('*********************************************')
		time.sleep(2)
		for trial_count in range(1, MAX_TRIAL_COUNTS + 1):
			print('Versuch ', trial_count, 'von', MAX_TRIAL_COUNTS)
	
			while True:
				try:
					guessed_number = int(input('>> Deine Zahl: '))
					break
				except ValueError:
					print('Ungültige Eingabe. Bitte wiederhole deine'
						' Eingabe...')
					
			if secret_number != guessed_number:
				print(
				'Die gesuchte Zahl ist {0} als {1}'.format(
				'größer' if secret_number > guessed_number else 'kleiner',
				guessed_number
					)
				)
			else:
				time_used = time.time() - start_time
				print('+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')
				print('+      Herzlichen Glückwunsch! Die gesuchte Zahl war {0}.      +'.format(guessed_number))
				print('+      Benötigte Versuche: ', trial_count, "                                +")
				print('+      Deine Zeit: {0:.2f} Sekunden oder ({0:.2f}/60) Minuten.    +'.format(time_used))
				print('+      Ergebnis erzielt:', time.strftime("%d.%m.%Y %H:%M:%S                  +"))
				print("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++")
				break
		else:
			print('-----------------------------------')
			print(player_name, ', du hast es leider nicht geschafft.')
			time.sleep(0.5)
			print('Versuche es am besten gleich nochmal!')
			time.sleep(0.5)

		print('\n\
		Bestätige mit [1] um fortzufahren.\n\
		Bestätige mit [2] um ins Konfigurationsmenü zu gelangen.\n\
		Bestätige mit [0] um das Programm zu beenden.')
		while True:
			response = input('>> Deine Auswahl: ')
			if response == '0':
				raise SystemExit
			elif response == '2':
				set_range(player_name)
			elif response == '1':
				play_game(player_name,MIN,MAX,MAX_TRIAL_COUNTS)


if __name__ == '__main__':
	main()
Also erstens, wer Lust und Zeit hat, und das Spiel mal ausprobieren möchte, sollte sich natürlich auch einmal das Spiel komplett anschauen, wer danach etwas "cheaten" mag, kann bei seinem Namen "TEST" eingeben und überspringt somit etwa 30 "print()"-Ausgaben und ca.(!) 120 Sekunden Programmdauer.
Desweiteren hab ich, da ich gemerkt hab, dass es bei einzelnen, wenigen Zahlen noch zu Endlos-Berechnungen (s.o.) kommen kann, vorerst erstmal alles auf 50er-Schritte ausgelegt.
Und da sind noch so EINIGE andere Kleinigkeiten, die mir nicht gefallen, die ich aber auch alle später noch in Angriff nehmen kann. Denn dadurch dass ich noch ganz am Anfang stehe, lerne ich ständig was neues hinzu und wenn ich hier dann erst was vorlegen möchte, wenn ich auch hundert prozentig mit zufrieden bin, dann dauert das wahrscheinlich noch ewig... ;-)

Ich muss leider schon längst wieder in der Falle sein, sonst würd ich jetzt noch auf einige Sachen eingehen, aber vielleicht noch kurz das wichtigste:



Für Tipps und Kritik bin ich wie immer offen! :-)

Hier auch nochmal ein großer Dank an Dich, BlackJack, denn dadurch dass du meinen vorigen chaos-Code mal ordentlich verpackt hast, und ich das ganze abgetippt hab, hab ich nochmal ne ganze Menge in Sachen Style und im Weglassen unnötiger Fehlerbehandlungen durch try/except INT() - Behandlungen gelernt -> einfach als str() '0' und die entsprechend if/elif/else-Anweisungen in eine while-Schleife packen! Schon spart man sich das ganze Drumrum.
(wo ist hier eigentlich der DaumenHochButton? ;-) )
(im Code oben sind beide Vorgehensweisen noch vorhanden, wird aber natürlich noch gefixt - ist im nächsten Update dann verschwunden)


Edit: "import sys" und "raise SystemExit" eingefügt.
Zuletzt geändert von Anonymous am Donnerstag 19. Dezember 2013, 09:00, insgesamt 1-mal geändert.
Grund: Quelltexte in Python-Code-Tags gesetzt.
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
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Moin,

bitte lagere doch so große Code-Blöcke in ein Paste-Bin aus; wir haben eines im Forum eingebaut (Link in oberer Menüleiste) oder Du nutzt z.B. gist.github.com.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@BSA: Wegen der Zeilennummern bei der Ausgabe zum Fehlersuchen/Nachverfolgen solltest Du Dich mal mit dem `logging`-Modul beschäftigen. Das kann so Sachen wie Modulname, Funktions-/Methodenname, und Zeilennummer automatisch einfügen, und das auch noch immer passend zum Quelltext. :-)

Die `time.sleep()`-Aufrufe sind echt komisch. Ich verstehe nicht so recht was die bringen sollen. Wenn ich mehr Zeit brauche Debugausgaben nachzuvollziehen, dann leite ich die Ausgabe in eine Datei um, dann habe ich hinterher alle Zeit der Welt mir das anzuschauen ohne dass man dafür das Programm künstlich verlangsamen muss.

Bei der Berechnung denkst Du viel zu kompliziert. Statt die Bisektion konkret nachzuvollziehen mit allen Zwischenwerten die man für eine tatsächliche Bisektion bräuchte, könntest Du Dir überlegen was dieses Verfahren etwas allgemeiner betrachtet macht: Es halbiert in jedem Schritt die Anzahl der in Frage kommenden Zahlen. Welche der beiden Hälften ausgewählt wird, ist für das zählen wie oft man die Kandidatenmenge um die Hälfte reduziert, ja völlig egal. Also kann man einfach ausrechnen wie viele Zahlen zwischen `MIN` und `MAX` liegen und diese Anzahl dann in einer Schleife so lange durch zwei teilen bis der Wert höchstens 1 ist. Die Anzahl der Schleifendurchläufe ist dann das gesuchte Ergebnis.

Und die logₙ-Funktion macht die Schleife überflüssig.

Code: Alles auswählen

from itertools import count
from math import ceil, log


def calculate_min_trials_2(a, b):
    x = b - a + 1
    for i in count():
        if x <= 1:
            return i
        x /= 2


def calculate_min_trials(a, b):
    return int(ceil(log(b - a + 1, 2)))


def main():
    for i in range(100000):
        if calculate_min_trials(0, i) != calculate_min_trials_2(0, i):
            print(i)


if __name__ == '__main__':
    main()
BlackJack

@BSA: Nochmal zum Quelltext:

Die Einrückung entspricht nicht dem Style Guide. Und an einigen Stellen würde ein Leerzeichen nach Kommas das ganze etwas lesbarer machen. Komplette Grosschreibung kennzeichnet per Konvention Konstanten. Funktionsargumente sind keine Konstanten.

Ein `import sys` macht nur Sinn, wenn man dann auch irgendetwas aus dem Modul verwendet. Zum Beispiel die `exit()`-Funktion, die man einem ``raise SystemExit`` vorziehen sollte.

`div_op_range` verstehe ich nicht. Erst mal ist das aufbauen einer solchen Liste mit `insert()` ziemlich unsinnig. Wenn man an eine Liste etwas anhängen will, dann nimmt man `append()`. Letztlich ist das eine Liste mit den Zahlen von 1 bis inklusive 40. Die erstellt man viel einfacher mit ``list(range(1, 41))``. Das einzige was Du damit dann aber machst ist linear Suchen ob eine bestimmte Zahl darin enthalten ist. *Das* kann doch aber einfacher direkt mit vergleichen ermitteln statt in einer Liste zu suchen. Was Du in `set_range()` eigentlich wissen möchtest ist ob die eingegebene Zahl durch 50 teilbar ist. Dazu ermittelt man einfach ob der der Rest beim Teilen durch 50 gleich 0 ist.

Diese ganzen `print()`/`time.sleep()`-Abfolgen sollte man in Datenstrukturen für die Texte und Zeitangaben und eine Funktion zur Ausgabe aufteilen. Eventuell würde sich hier auch schon eine Klasse lohnen mit Methoden zur Ausgabe und zum setzen der Verzögerung.

Der '\' zur Fortsetzung einer logischen Zeile ist nur notwendig wenn der Compiler das ohne dieses Zeichen nicht selbst erkennen könnte. Solange es noch offene Klammern gibt, braucht man den nicht.

Bei `set_difficulty()` ist ein Fehler im Programm. Wenn man eine ungültige Eingabe vornimmt bricht die ``while``-Schleife trotzdem ab und `max_trial_counts` ist nicht definiert, was zu einer Ausnahme führt.

Der Code ist falsch strukturiert. Es gibt keine einzige Funktion. Du missbrauchst Funktionsdefinitionen als Sprungmarken und Funktionsaufrufe als „GOTO”-Befehl zu einem Codeblock mit einem Namen. So ist das nicht gedacht und deswegen hast Du auch das Problem mit dem Programmabbruch und das Programm bricht irgendwann mit einer Fehlermeldung ab weil der Aufrufstapel voll ist, weil die Funktionsaufrufe nie zurückkehren.

Eine Funktion die nicht zum Aufrufer zurückkehrt ist in der Regel ein Fehler.
BSA
User
Beiträge: 38
Registriert: Freitag 6. Dezember 2013, 07:49

Moin Leute,

ja, das auslagern ähnlich großer Blöcke klingt vernünftig, werd ich beim nächsten Mal so machen.

@BlackJack: Ich muss gestehen, ich sehe ein, dass die von dir erstellte Funktion kurz, knapp und zielführend ist. Ich habe sie noch nicht selber ausprobiert, muss allerdings auch gestehen, dass ich sie vom bloßen lesen nicht verstehe. Ich werd mich aber mal mit ihr beschäftigen...
Die Umleitung der Ausgaben in eine Datei (am besten mit den Funktionen bzw. Ausgaben des logging-Moduls) finde ich eine sehr gute Idee! Ich werde bei der nächsten ähnlich gelagerten Problemstellung darauf zurückkommen und das sehr gerne ausprobieren.
Statt die Bisektion konkret nachzuvollziehen mit allen Zwischenwerten die man für eine tatsächliche Bisektion bräuchte, könntest Du Dir überlegen was dieses Verfahren etwas allgemeiner betrachtet macht: Es halbiert in jedem Schritt die Anzahl der in Frage kommenden Zahlen. Welche der beiden Hälften ausgewählt wird, ist für das zählen wie oft man die Kandidatenmenge um die Hälfte reduziert, ja völlig egal. Also kann man einfach ausrechnen wie viele Zahlen zwischen `MIN` und `MAX` liegen und diese Anzahl dann in einer Schleife so lange durch zwei teilen bis der Wert höchstens 1 ist.
Ich muss eingestehen: genau das habe ich versucht. Über die beiden if-Bedingungen wollte ich nach der ersten Lokalisierung (if</>) und nach jedem Einzelschritt (x<y bzw x>y) erfahren, welche neue Untergrenze und Obergrenze festgestellt werden kann. Die einfachste Möglichkeit, diesen "Eingrenzungsbereich" zu "zählen", ist mir bei der len()-Funktion aufgefallen. DIesen Wert habe ich als Integer halbiert und das Procedere von vorne durchlaufen lassen.

Zu den time.sleep()-Aufrufen kann ich nur sagen: Amateure am Werk! :-)
Die Umleitung benötigter Ausgaben in eine Datei ist eine gute Idee. An der Umsetzung werde ich mich aber noch versuchen müssen! Muss mich da dann wahrscheinlich noch einarbeiten...

Mit dem StyleGuide habe ich mich bisher noch gar nicht auseinandergesetzt. Erstmal möchte ich mich auch eher an der OOP, also Klassen & co versuchen. Reinlesen kann ich aber natürlich schonmal.
Das Leerzeichen nach Kommata werde ich ab sofort gerne umsetzen. Ich dachte, es wäre "Ressorcenschonender", da ja ein Byte-Code (bzw. ein Zeichen...) weniger erzeugt würde... ;-)
Stimmt, die Großschreibung hätte ich (bis auf für MIN) vermeiden können, habe aber nicht dran gedacht das zu ändern - unschön!

Ja, ich muss sagen, schön sieht das mit den ewig langen Text+time.sleep() -Zeilen nicht wirklich aus - ich werd die Texte mal bei Zeiten in eine Datei schreiben und schauen wie ich die jeweiligen Passagen über eine eigens erstellte "Ausgabe"-Klasse in einem entsprechend langsamen Tempo ausgeben lassen kann - oder ob ich Textblöcke ausgeben lasse, die jeweils mit "weiter" bestätigt werden müssen, was einige Vorteile bringt, da ja nicht jeder Mensch gleich schnell liest... (obwohl ich dieses Spiel schon eher für mich schreibe, als für andere...es geht ja im Grunde um die Übung)
Da kann ich mich dann auch gaaanz langsam mal an die OOP ranwagen, mit einer ziemlich einfach gestrickten Klasse, was sich sicher schon irgendwie machen lassen sollte!

Danke für den Hinweis ('set_difficulty') ! War mir gar nicht aufgefallen...wird natürlich umgemodelt!

Der richtig harte Brocken für mich kommt erst jetzt. Ich wüsste nämlich aktuell nicht, wie ich das ganze strukturieren sollte, so dass jede aufgerufene Funktion auch wieder zur jeweils aufrufenden Funktion zurückkehren sollte...das stellt mich echt vor ein logisches Problem. SIcherlich nicht unlösbar..., aber im Moment fiele mir dazu nichts gescheites ein. Ein nettes Puzzle also...wobei sich das ganze durch eine geschickte Kombo einer übergeordneten Klasse mit den Methoden "Ausgabe", "Spiel" und "Einstellungen" eigentlich evtl. - vielleicht - doch recht "angenehm" lösen lassen dürfte. Dafür müsste ich mich aber nochmal ransetzen und mich komplett einüben/-lesen...was vielleicht ja auch gar nicht soo verkehrt wär...

Ich werde versuchen, das alles irgendwie in der nächsten Zeit umzusetzen.
Zugleich möchte ich mich auch in die Grundfunktionen von tkinter "einarbeiten", um das ganze ein wenig aus dem Terminal rauszuholen.

Meine Ziele für das nächste "release":

* Code komplett überarbeiten/restrukturieren
* mind. eine Klasse "Ausgabe", evtl. gar eine Klasse "Spiel" mit den Methoden 'spielen', 'ausgeben' und 'einstellen' definieren
* sämtliche vordefinierten Texte in irgendeine Datei (für den Anfang wohl .txt) eingeben und von dort Häppchenweise auslesen lassen
* evtl. eine Highscore-Datei erstellen (evtl. als Methode der Klasse Spiel, sofern ich sie einbaue...) -> 1. Highscore-Formel | 2. Highscore-Datei | 3. Bestenliste mit entsprechendem, einfachen Datensatz (.txt oder .css)
* das ganze versuchen über eine einfache GUI per tkinter ablaufen zu lassen


Ob ich damit dieses Jahr überhaupt noch fertig werden sollte, wage ich mal dezent zu bezweifeln - aber das macht ja nix :)

So. Dann Danke ich nochmal für die reichlichen Hinweise, auch wenn sie eine ganze Menge Arbeit bedeuten... : ich mach's ja freiwillig ;-)

Viele Grüße
BSA
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
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Zur Strukturierung: Es ist immer ganz gut, Funktionen zu haben, die sozusagen auf einer höheren Ebene stehen und die ihrerseits Funktionen aus niedrigeren Ebenen aufrufen.

So könnte deine ``play_game()`` die oberste Ebene darstellen und in einer Endlosschleife zum Beispiel zunächst eine noch zu erstellende ``show_main_menu()`` aufrufen. Diese Unterfunktion würde das Hauptmenü auf dem Bildschirm ausgeben den vom User ausgewählten Menüpunkt als Zahl zurückliefern.

Dann wäre die ``play_game()`` wieder am Zug, da die Zeile mit der Unterfunktion ja abgearbeitet wurde. Es würde nun in ``play_game()`` aufgrund der Benutzerwahl entschieden werden, ob die Konfigurationauswahl ausgeführt wird, ob das Spiel gestartet wird oder ob das Programm beendet wird.

Ein Programmende würde ich einfach durch ein ``return`` aus der ``play_game()``-Funktion heraus signalisieren und nicht durch einen expliziten Aufruf für das Programmende.

Der Aufruf für die Konfiguration sollte IMHO nicht automatisch das Spiel starten (wie es bei dir momentan ja der Fall ist). Vielmehr sollte nach dem Durchlauf der Konfiguration wieder das Hauptmenü angezeigt werden. Und dies geschieht eben nicht durch einen erneuten Aufruf von ``play_game()``, sondern indem der Konfigurationsablauf einfach irgendwann fertig geworden ist. Dann nämlich befindet sich der Programmfluss wieder in der ``play_game()``.

Bei entsprechender Strukturierung beginnt in der ``play_game()`` dann einfach der nächste Durchlauf deiner Endlosschleife, wo halt wieder mittels ``show_main_menu()`` das Hauptmenü angezeigt wird. Und dann könnte der User entscheiden, ob er nun das Spiel starten möchte. So kennt man das ja normalerweise auch von anderen Spielen her.

Du hättest mit diesem Vorgehen einerseits eine gesteigerte Übersichtlichkeit. Und andererseits wird das standardmäßig eingestellte Rekursionslimit nicht überschritten, da eine Unterfunktion nun nicht mehr die Funktion aus der höheren Ebene benutzt, von der aus ja abermals die Unterfunktion verwendet wird. Du hättest damit das ewige Hin und Her vermieden, bei dem nämlich der erste Aufruf (und alle weiteren Aufrufe auch) niemals erfährt, was das Ergebnis des Aufrufs ist, da anstatt einer Ausgabe des Ergebnisses (oder zumindest Rückgabe des Kontrollflusses) von der Unterfunktion ja einfach ein *neuer* Aufruf der Hauptfunktion getätigt wird. Und nur weil es die gleiche Funktion ist, weiß trotzdem der eine Funktionsaufruf nichts vom anderen. Das heißt, der erste Aufruf wartet auf sein Ergebnis bis er schwarz wird. Ich hoffe, dir ist die Problematik hiermit etwas klarer geworden... ;)

Und damit sind übrigens auch die Zuständigkeiten bzw Kompetenzen der einzelnen Funktionen klarer geregelt. Im Grunde tut man einfach so, als sei es einer Unterfunktion untersagt, Funktionen aufzurufen, die über ihr stehen. Das ist ein recht hilfreiches Konzept beim Programmieren.

EDIT: Sorry, ich schrieb oben etwas davon, dass nach der Konfiguration sofort das Spiel gestartet würde. Das stimmt natürlich nicht. Es wird ja lediglich ``play_game()`` aufrufen, welches bei dir wieder das Hauptmenü anzeigt. Trotzdem ist der Aufruf von ``play_game()`` in der letzten Zeile der ``set_difficulty()``-Funktion aus den oben genannten Gründen nicht zu empfehlen.
BSA
User
Beiträge: 38
Registriert: Freitag 6. Dezember 2013, 07:49

So, ich hab dann nochmal ein wenig an meinem Projekt gearbeitet.
Es sind einige Funktionen dazugekommen.
Wer die Sound-Dateien und/oder die .txt-Datei möchte möge einfach bescheid geben.

Das Projekt ist noch lange nicht fertig. Es sollen noch eine HighScore-Funktion und eine GUI irgendwann hinzukommen, weiteres steht noch nicht fest.

Zu den Funktionen random_str() und str_output() vielleicht noch eine kurze Erklärung:
Da ich das Gefühl hatte, dass eine einfache random.randint(1,4)-Anweisung zu oft die gleichen Zahlen hintereinander ausgab, habe ich das ganze ein wenig weitergestrickt.
Ob es mathematisch einen Sinn macht, möchte und kann ich jetzt so nicht beurteilen. Das überlasse ich gerne euch. Subjektiv aber habe ich das Gefühl, dass es so nun besser funktioniert als mit nur vier Möglichkeiten.
Wie gesagt, dass ist natürlich nur mein subjektives Empfinden... :-)

Mir sind nach etlichem hin und her selber keine Laufzeitfehler mehr aufgefallen, aber ob die Programmlogik soweit akzeptabel und auch sonst alles im Lot ist, weiß ich natürlich nicht. Auch kann es natürlich sein, dass euch Laufzeitfehler auffallen, allein aus dem Code heraus...


Wie immer freue ich mich also auf eure Hinweise, Kritik und Verbesserungsvorschläge. Insbesondere habe ich das Gefühl, dass der Code unnötig lang ist, sehe da aber nicht mehr viel Spielraum... :?:

@ sirius3: Die play_sound()-Funktion habe ich jetzt noch nicht eingebaut. Sie kommt bei der nächsten Veröffentlichung mit rein.

Grüße
BSA


PS: Es ist keine Klasse mehr vorhanden. Das ist erstmal auf die lange Bank verschoben - vorher müsste ich wohl etliche Beispiele durchexerzieren, was ich dann nochmal machen werde.

Für den Code, Klick mich
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
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@BSA: Deine »random_str« kannst Du in einen Einzeiler verwandeln:

Code: Alles auswählen

>>> random.seed(0)
>>> [random_str() for k in range(99)]
[1, 1, 4, 1, 2, 4, 2, 4, 1, 3, 1, 3, 1, 3, 1, 1, 2, 4, 3, 2, 3, 2, 1, 3, 2, 4, 3, 1, 4, 3, 1, 4, 2, 1, 1, 4, 1, 1, 3, 3, 1, 1, 1, 1, 3, 2, 2, 3, 3, 4, 4, 2, 2, 4, 1, 2, 4, 3, 2, 1, 2, 1, 2, 1, 1, 4, 2, 2, 3, 1, 1, 4, 2, 1, 4, 2, 4, 1, 1, 4, 4, 1, 1, 1, 2, 2, 4, 3, 3, 2, 3, 1, 3, 3, 3, 2, 3, 3, 2]
>>> random.seed(0)
>>> [random.randint(1,512)%4 for k in range(99)]
[1, 1, 0, 1, 2, 0, 2, 0, 1, 3, 1, 3, 1, 3, 1, 1, 2, 0, 3, 2, 3, 2, 1, 3, 2, 0, 3, 1, 0, 3, 1, 0, 2, 1, 1, 0, 1, 1, 3, 3, 1, 1, 1, 1, 3, 2, 2, 3, 3, 0, 0, 2, 2, 0, 1, 2, 0, 3, 2, 1, 2, 1, 2, 1, 1, 0, 2, 2, 3, 1, 1, 0, 2, 1, 0, 2, 0, 1, 1, 0, 0, 1, 1, 1, 2, 2, 0, 3, 3, 2, 3, 1, 3, 3, 3, 2, 3, 3, 2]
Hier ist nur 4 durch 0 ersetzt. Qualitativ sind das die gleichen Zufallszahlen wie bei randint(1,4).

Der '_' vor Variablennamen deutet an, dass es sich um eine nicht verwendete Variable handelt. Bei Dir ist das aber nicht der Fall. Ich glaube zu »text_remder« wurden schon einige Kommentare abgegeben: Du kannst direkt über »testfile« iterieren und mußt nicht erst die ganze Datei in »text_lines« einlesen. »exit« in einer Funktion ist keine gute Idee. Die aufrufende Funktion sollte entscheiden, was passiert, wenn es einen Lesefehler gibt, dann kannst Du »text_render« flexibler wiederverwenden (In einem GUI wäre eine Textausgabe zum Beispiel unsinnig). Das »format« beim »print« ist unnötig. Zieh das »if« aus der Schleife und setzt da eine Variable »delay« die Du im Aufruf von «time.sleep» verwenden kannst.
BlackJack

@BSA: Ebenfalls ein unkonventioneller Name ist `MAX` in `play_game()`. Der ist sowieso recht überflüssig weil da nur ein Funktionsargument dran gebunden wird. Und dann wird der Name nur einmal verwendet und das Funktionsargument wird innerhalb der Funktion auch nicht neu gebunden, also kann man an der einen Stelle wo `MAX` verwendet wird auch `ciper_band` verwendet werden.

Die Funktion ist vielleicht auch klein wenig zu umfangreich. Und wenn ich das richtig sehe ist da schon wieder (oder immer noch) eine unnötige Schleife, nämlich gleich die erste ``while``-Schleife die den gesamten Rest der Funktion beinhaltet. Als letztes in dieser Schleife wird die Funktion nämlich in jedem Fall mit einem ``return`` verlassen, die Schleife wird also grundsätzlich nur einmal ausgeführt, und damit ist sie als Schleife sinnfrei.

Die `bisect()`-Funktion verwendet einen Haufen unschöner Namen. Über die Funktion hatten wird doch schon mal geredet. Das ist entweder eine einfach Schleife in der gezählt wird wie oft man den Suchraum halbieren kann — ohne das man da tatsächlich irgendwelche Enden eines halbierten Intervalls bestimmen müsste, oder eine einfache geschlossene Formel, also ein Einzeiler: ``return int(math.ceil(math.log(cipher_band)))``.

Das mit den zufälligen Zeichenketten ist viel zu umständlich und indirekt gelöst. Pack die Zeichenketten in Listen und wähle zufällig ein Element mit `random.choice()` aus. Es gibt keinen Grund da selber erst eine Zufallszahl zu erzeugen, das auch noch für Computer ungewöhnlich mit der 1 statt der 0 beginnend, und dann eine Funktion mit einer Zahl zur Auswahl der Liste und der Zufallszahl aufzurufen. Das ist total umständlich, unflexibel, und fehleranfällig weil schwer verständlich.
BSA
User
Beiträge: 38
Registriert: Freitag 6. Dezember 2013, 07:49

Hallo BlackJack,

erstmal Danke für deine Antworten! Nur so kann man sich verbessern und sieht noch grobe Fehler / manchen Unsinn ;-)

MAX ist natürlich sofort raus, da habe ich wohl was übersehen :oops:
Auch die Schleife in playgame() habe ich auf deinen Hinweis hin rausgenommen. Ist zwar irgendwo logisch, war mir aber ehrlich gesagt gar nicht aufgefallen...gut zu wissen!
Ebenso habe ich die 'sys.exit()'-Anweisung rausgenommen und eine elegantere (und viel einfachere...) Lösung in text_render() verwendet. Auch habe ich natürlich sofort deinen Tipp mit der Variablen 'delay' umgesetzt.

Zu dem Pseudo-Zufallsgenerator bleibt nicht viel zu sagen, außer evtl...:
Sowas hatte ich mir schon gedacht, war mir aber natürlich nicht 100%ig sicher. Mittlerweile habe ich mich minimal eingelesen und soweit ich das richtig verstanden habe, nutzt random() wohl den "urandom"-Zufallsgenerator von meinem Linux, und an DEN komme ich sicher nicht ran ;-)
Ich habe also auch hier deinen Tipp mit random.choice umgesetzt. Die zugehörige Funktion ist jetzt inkl. 'def random_str():' etwa ungefähr 5 Zeilen lang... (output_random ist dort integriert, die '_' somit verschwunden)

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. Und da ich in meinem eigenen Programm nichts verwenden möchte, was ich nicht selber auch verstehe, habe ich dies mal vorerst ausgelassen. Ist natürlich trotzdem abgespeichert !
Ich werd mich bei Zeiten evtl. nochmal mit der allg. (Schul-)Mathematik auseinandersetzen...diese Warte-Bank ist allerdings auch erstmal die längste :-)
Beim ausprobieren ist mir allerdings noch ausgefallen: bei 'print(math.ceil(math.log(100)))' bekomme ich als Ergebnis eine '5' - ?

Aktuell ist mir heute noch aufgefallen, dass meine Ausnahmebehandlung für den IOError der Sounddateien in der main() funktionslos war, da pygame einen Error ausruft. Ich habe dies dann also in 'except pygame.error' umgeändert.
Seitdem bin ich der Idee hinterher, das ganze in eine Funktion 'loading_sounds' (o.dgl...) auszulagern und dabei dann die fehlende Datei dem Nutzer mit auszugeben, damit dieser sich diese (so die Theorie...) aus dem Netz nachladen und in den Ordner ablegen kann.
Es soll dabei bleiben, dass wenn eine Sounddatei nicht gefunden wird, keine Sounds abgespielt werden. Jedoch soll dem Nutzer eben ausgegeben werden, welche Datei dies ist.
Das habe ich vorhin nicht mehr fertig gekriegt, werde mich aber evtl. die Tage nochmal damit beschäftigen.
Der Ablauf schwebt mir schon vor Augen, nur an den konkreten Anweisungen probiere ich noch rum. Das macht einerseits Spaß, andererseits lernt man daran, finde ich, auch eine Menge bzw. bekommt ein Gefühl für die Logiken die dahinter stecken.

Die text_render habe ich übrigens, soweit ich das richtig verstanden haben sollte, nun hoffentlich richtig umgesetzt. Meiner Meinung nach wird nun direkt über 'textfile' iteriert, versprechen kann ich das natürlich nicht.

Ich werde alle fertigen Änderungen morgen noch mal hier hochladen.


Danke also nochmal für deine Hinweise! :-)

BSA
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
Antworten