diff --git a/Uebung5/Kartenzaehler.py b/Uebung5/Kartenzaehler.py new file mode 100644 index 0000000..3ada5fe --- /dev/null +++ b/Uebung5/Kartenzaehler.py @@ -0,0 +1,493 @@ +import socket +import json +import time +import sys + +# Globale Konfiguration (Kommandozeilenargumente) +KARTEZAEHLER_LISTEN_IP = '127.0.0.1' # Wird überschrieben +KARTEZAEHLER_LISTEN_PORT = 0 # Wird überschrieben + +CROUPIER_IP = '127.0.0.1' # Wird überschrieben +CROUPIER_PORT = 0 # Wird überschrieben + +SPIELER_IP = '127.0.0.1' # Wird überschrieben +SPIELER_PORT = 0 # Wird überschrieben + +# Kartenumwandlung +def _card_from_str_counter(card_str): + # Umwandlung von Karten-Strings + if card_str is None or card_str == "HIDDEN": + return None + + # Karten mit 10 als 'S10', 'H10', etc. + if len(card_str) > 2 and card_str[1:3] == '10': + suit = card_str[0] + rank = '10' + else: + suit = card_str[0] + rank = card_str[1:] + + # Umwandlung von Abkürzungen + if rank == 'B': rank = 'J' # Bube + if rank == 'D': 'Q' # Dame + if rank == 'K': rank = 'K' # König + if rank == 'A': rank = 'A' # Ass + + return {'suit': suit, 'rank': rank} + + +class Kartenzaehler: + #Klasse implementiert die Logik für einen BlackJack-Kartenzähler + def __init__(self, listen_ip, listen_port, croupier_ip, croupier_port, spieler_ip, spieler_port): + # Initialisiert eine neue Instanz des Kartenzählers + # Zustandsvariablen für das Kartenzählen und die Statistik + self.anzahl_decks = 0 # Anzahl der Decks initialisiert vom Croupier + self.laufender_zaehlwert = 0 # Der aktuelle "Running Count" (Summe der HI-LO Werte der gespielten Karten) + self.anzahl_karten_gespielt = 0 # Gesamtanzahl der Karten + self.historie_karten = [] # Eine Liste der bisher gespielten Karten + + # Statistiken pro Spieler + self.statistik_gewinne = {} # Zählt Anzahl der gewonnenen Runden pro Spieler-ID + self.statistik_blackjacks = {} # Zählt Anzahl der Blackjacks pro Spieler-ID + self.statistik_runden_gespielt = {} # Zählt Gesamtanzahl der gespielten Runden pro Spieler-ID + + # Netzwerk-Setup + self.udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Erstellt einen UDP-Socket + try: + self.udp_socket.bind((listen_ip, listen_port)) # Bindet den Socket an die angegebene IP und den Port + print(f"[Kartenzaehler] Lauscht auf {listen_ip}:{listen_port}") + except OSError as e: + # Fehlerbehandlung + print(f"[Kartenzaehler] Fehler beim Binden des Sockets an {listen_ip}:{listen_port}: {e}") + sys.exit(1) # Beendet das Programm bei einem kritischen Fehler + + self.croupier_addr = (croupier_ip, croupier_port) # Speichert die Adresse des Croupiers + self.spieler_addr = (spieler_ip, spieler_port) # Speichert die Adresse des Spielers + + # HI-LO Zählwerte + self.HI_LO_WERTE = { + '2': 1, '3': 1, '4': 1, '5': 1, '6': 1, + '7': 0, '8': 0, '9': 0, + '10': -1, 'J': -1, 'Q': -1, 'K': -1, 'A': -1, + 'Bube': -1, 'Dame': -1, 'König': -1, 'Ass': -1 # Redundante Einträge + } + + def karten_verarbeiten(self, karten_str_liste): + #Verarbeitet eine Liste von Karten-Strings, aktualisiert den laufenden Zählwert und Anzahl der gespielten Karten + for karte_str in karten_str_liste: + if karte_str == "HIDDEN": + # Versteckte Karten werden nicht gezählt + continue + karte_obj = _card_from_str_counter(karte_str) # String in Kartenobjekt um + if karte_obj and karte_obj['rank'] in self.HI_LO_WERTE: + # Addiert HI-LO Wert der Karte zum laufenden Zählwert + self.laufender_zaehlwert += self.HI_LO_WERTE[karte_obj['rank']] + self.anzahl_karten_gespielt += 1 # Inkrementiert die Anzahl der gespielten Karten + self.historie_karten.append(karte_obj) # Fügt die Karte der Historie hinzu + print(f"[Kartenzaehler] Karten verarbeitet. Laufender Zaehlwert: {self.laufender_zaehlwert}, Gespielte Karten: {self.anzahl_karten_gespielt}") + + def true_count_berechnen(self): + #Berechnet den "True Count" basierend auf dem laufenden Zählwert + + if self.anzahl_decks == 0: + return 0.0 + + gesamt_karten_im_schuh = self.anzahl_decks * 52 # Gesamtzahl der Karten + verbleibende_karten = gesamt_karten_im_schuh - self.anzahl_karten_gespielt # Anzahl der noch nicht gespielten Karten + + if verbleibende_karten <= 0: + return 0.0 # Keine Karten mehr übrig + + # Schätzung der verbleibenden Decks + verbleibende_decks_schaetzung = verbleibende_karten / 52.0 + + if verbleibende_decks_schaetzung < 0.5: + verbleibende_decks_schaetzung = 0.5 + + # Der True Count ist der laufende Zählwert + return self.laufender_zaehlwert / verbleibende_decks_schaetzung + + def aktion_empfehlen(self, spieler_hand_werte, croupier_offene_karte_wert, true_count): + #Empfiehlt die optimale Aktion (Hit, Stand, Double Down, Split, Surrender) basierend auf der Basisstrategie des BlackJack und den "Abweichungen" + + hand_sum = sum(spieler_hand_werte) # Summe der Kartenwerte + + + is_pair = len(spieler_hand_werte) == 2 and spieler_hand_werte[0] == spieler_hand_werte[1] + + + is_soft = False + if 11 in spieler_hand_werte: # Wenn ein Ass vorhanden ist + current_sum = hand_sum + temp_num_aces = spieler_hand_werte.count(11) # Zählt die Anzahl der Asse + while current_sum > 21 and temp_num_aces > 0: + current_sum -= 10 # Wandelt ein Ass von 11 in 1 um + temp_num_aces -= 1 + if current_sum <= 21 and spieler_hand_werte.count(11) > 0: + is_soft = True + + if is_pair and spieler_hand_werte[0] == 11: + is_soft = True + + # Abweichungen von der Basisstrategie basierend auf dem True Count + if hand_sum == 16 and croupier_offene_karte_wert == 10 and not is_soft and not is_pair: + if true_count >= 0: + return "STAND" + + if hand_sum == 15 and croupier_offene_karte_wert == 10 and not is_soft and not is_pair: + if true_count >= 4: + return "STAND" + + if hand_sum == 10 and croupier_offene_karte_wert == 10 and not is_soft: + if true_count >= 4: + return "DOUBLE_DOWN" + + if hand_sum == 12 and croupier_offene_karte_wert == 2 and not is_soft: + if true_count >= 3: + return "STAND" + + if hand_sum == 12 and croupier_offene_karte_wert == 3 and not is_soft: + if true_count >= 2: + return "STAND" + + if hand_sum == 11 and croupier_offene_karte_wert == 11 and not is_soft: + if true_count >= 1: + return "DOUBLE_DOWN" + + if hand_sum == 9 and croupier_offene_karte_wert == 7 and not is_soft: + if true_count >= 3: + return "HIT" + + if hand_sum == 19 and is_soft and croupier_offene_karte_wert == 4: + if true_count <= -2: + return "HIT" + + # 1. Split-Entscheidungen (Basisstrategie für Paare) + if is_pair: + pair_rank_value = spieler_hand_werte[0] + + if pair_rank_value == 11: + return "SPLIT" + elif pair_rank_value == 8: + return "SPLIT" + elif pair_rank_value == 2 or pair_rank_value == 3: + if croupier_offene_karte_wert >= 2 and croupier_offene_karte_wert <= 7: + return "SPLIT" + elif pair_rank_value == 4: + if croupier_offene_karte_wert in [5, 6]: + return "SPLIT" + elif pair_rank_value == 5: + pass + elif pair_rank_value == 6: + if croupier_offene_karte_wert >= 2 and croupier_offene_karte_wert <= 6: + return "SPLIT" + elif pair_rank_value == 7: + if croupier_offene_karte_wert >= 2 and croupier_offene_karte_wert <= 7: + return "SPLIT" + elif pair_rank_value == 9: + if croupier_offene_karte_wert not in [7, 10, 11]: + return "SPLIT" + elif pair_rank_value == 10: + return "STAND" + + # 2. Harte Entscheidungen (kein Ass) + if not is_soft: + if hand_sum >= 17: + return "STAND" + elif hand_sum == 16: + if croupier_offene_karte_wert >= 2 and croupier_offene_karte_wert <= 6: + return "STAND" + elif croupier_offene_karte_wert == 10 or croupier_offene_karte_wert == 11: + return "SURRENDER" + else: + return "HIT" + elif hand_sum == 15: + if croupier_offene_karte_wert >= 2 and croupier_offene_karte_wert <= 6: + return "STAND" + elif croupier_offene_karte_wert == 10 or croupier_offene_karte_wert == 11: + return "SURRENDER" + else: + return "HIT" + elif hand_sum == 14 or hand_sum == 13: + if croupier_offene_karte_wert >= 2 and croupier_offene_karte_wert <= 6: + return "STAND" + else: + return "HIT" + elif hand_sum == 12: + if croupier_offene_karte_wert >= 4 and croupier_offene_karte_wert <= 6: + return "STAND" + else: + return "HIT" + elif hand_sum == 11: + return "DOUBLE_DOWN" + elif hand_sum == 10: + if croupier_offene_karte_wert >= 2 and croupier_offene_karte_wert <= 9: + return "DOUBLE_DOWN" + else: + return "HIT" + elif hand_sum == 9: + if croupier_offene_karte_wert >= 3 and croupier_offene_karte_wert <= 6: + return "DOUBLE_DOWN" + else: + return "HIT" + else: + return "HIT" + + # 3. Weiche Entscheidungen (Ass zählt als 11) + if is_soft: + if hand_sum >= 19: + return "STAND" + elif hand_sum == 18: + if croupier_offene_karte_wert >= 2 and croupier_offene_karte_wert <= 8: + if croupier_offene_karte_wert in [2,7,8]: + return "STAND" + elif croupier_offene_karte_wert in [3,4,5,6]: + return "DOUBLE_DOWN" + else: + return "HIT" + else: + return "HIT" + elif hand_sum == 17: + if croupier_offene_karte_wert >= 3 and croupier_offene_karte_wert <= 6: + return "DOUBLE_DOWN" + else: + return "HIT" + elif hand_sum == 16: + if croupier_offene_karte_wert >= 4 and croupier_offene_karte_wert <= 6: + return "DOUBLE_DOWN" + else: + return "HIT" + elif hand_sum == 15: + if croupier_offene_karte_wert >= 4 and croupier_offene_karte_wert <= 6: + return "DOUBLE_DOWN" + else: + return "HIT" + elif hand_sum == 14 or hand_sum == 13: + if croupier_offene_karte_wert >= 5 and croupier_offene_karte_wert <= 6: + return "DOUBLE_DOWN" + else: + return "HIT" + else: + return "HIT" + + return "STAND" + + def optimale_einsatzhoehe_empfehlen(self, true_count): + #Empfiehlt die optimale Einsatzhöhe basierend auf dem True Count + + BASE_BET_UNIT = 50 + + if true_count <= 0: + return BASE_BET_UNIT + elif true_count >= 1 and true_count < 2: + return BASE_BET_UNIT * 1.5 + elif true_count >= 2 and true_count < 3: + return BASE_BET_UNIT * 2 + elif true_count >= 3 and true_count < 4: + return BASE_BET_UNIT * 3 + elif true_count >= 4: + return BASE_BET_UNIT * 4 + + return BASE_BET_UNIT + + def statistik_aktualisieren(self, spieler_id, gewonnen, hatte_blackjack): + #Aktualisiert die Gewinnstatistiken für einen bestimmten Spieler + self.statistik_runden_gespielt[spieler_id] = self.statistik_runden_gespielt.get(spieler_id, 0) + 1 + if gewonnen: + self.statistik_gewinne[spieler_id] = self.statistik_gewinne.get(spieler_id, 0) + 1 + if hatte_blackjack: + self.statistik_blackjacks[spieler_id] = self.statistik_blackjacks.get(spieler_id, 0) + 1 + + def gib_statistiken_an_croupier(self, spieler_id=None): + #Gibt die Statistiken für einen bestimmten Spieler oder für alle Spieler zurück + if spieler_id: + gewinne = self.statistik_gewinne.get(spieler_id, 0) + runden = self.statistik_runden_gespielt.get(spieler_id, 0) + gewinnrate = (gewinne / runden) * 100 if runden > 0 else 0 + return { + "player_id": spieler_id, + "gewinne": gewinne, + "blackjacks": self.statistik_blackjacks.get(spieler_id, 0), + "runden": runden, + "gewinnrate": f"{gewinnrate:.2f}%" + } + else: + all_stats = {} + # Iteriert über alle Spieler-IDs, für die Runden gespielt wurden, und ruft deren Statistiken ab + for p_id in self.statistik_runden_gespielt.keys(): + all_stats[p_id] = self.gib_statistiken_an_croupier(p_id) + return all_stats + + def reset_runden_statistik(self): + #Setzt die Zählwerte + self.laufender_zaehlwert = 0 + self.anzahl_karten_gespielt = 0 + self.historie_karten = [] + print("[Kartenzaehler] Zaehlwerte zurueckgesetzt. Statistiken bleiben bestehen.") + + def _send_udp_message(self, target_ip, target_port, message_dict): + #Hilfsfunktion zum Senden einer UDP-Nachricht + try: + message_json = json.dumps(message_dict).encode('utf-8') + self.udp_socket.sendto(message_json, (target_ip, target_port)) + print(f"[Kartenzaehler] -> Gesendet an {target_ip}:{target_port}: Typ='{message_dict.get('type')}' Payload={str(message_dict.get('payload', ''))[:50]}...") + except Exception as e: + print(f"[Kartenzaehler] Fehler beim Senden an {target_ip}:{target_port}: {e}") + + def empfangen(self): + + try: + data, addr = self.udp_socket.recvfrom(4096) + nachricht = json.loads(data.decode('utf-8')) + return nachricht, addr + except socket.timeout: + + return None, None + except json.JSONDecodeError as e: + print(f"[Kartenzaehler] Fehler beim Decodieren der JSON-Nachricht von {addr}: {e}, Daten: {data.decode('utf-8', errors='ignore')}") + return None, None + except Exception as e: + print(f"[Kartenzaehler] Fehler beim Empfangen von UDP-Nachricht: {e}") + return None, None + + def verarbeite_eingehende_nachricht(self, nachricht, sender_adresse): + #Verarbeitet eine empfangene Nachricht basierend auf ihrem Typ und dem Absender + msg_type = nachricht.get("type") + payload = nachricht.get("payload", {}) + + print(f"[Kartenzaehler] <- Empfangen von {sender_adresse[0]}:{sender_adresse[1]}: Typ='{msg_type}' Payload={payload}") + + if sender_adresse == self.croupier_addr: + # Nachrichten vom Croupier + if msg_type == "cards_dealt_to_counter": + ausgegebene_karten_str = payload.get("cards", []) + self.karten_verarbeiten(ausgegebene_karten_str) + print(f"[Kartenzaehler] Aktueller True Count: {self.true_count_berechnen():.2f}") + + elif msg_type == "game_start_info": + self.anzahl_decks = payload.get("num_decks") + self.reset_runden_statistik() + print(f"[Kartenzaehler] Spiel initialisiert: {self.anzahl_decks} Decks.") + + elif msg_type == "round_result_for_counter": + spieler_id = payload.get("player_id") + gewonnen = payload.get("won", False) + hatte_blackjack = payload.get("had_blackjack", False) + self.statistik_aktualisieren(spieler_id, gewonnen, hatte_blackjack) + print(f"[Kartenzaehler] Statistik aktualisiert fuer Spieler {spieler_id}: Gewonnen={gewonnen}, Blackjack={hatte_blackjack}") + + elif msg_type == "statistics_request_from_croupier": + # Croupier fordert Statistiken an + spieler_id_angefragt = payload.get("player_id", None) + statistiken = self.gib_statistiken_an_croupier(spieler_id_angefragt) + # Sendet die angeforderten Statistiken zurück an den Croupier + self._send_udp_message(self.croupier_addr[0], self.croupier_addr[1], { + "type": "statistics_response_to_croupier", + "payload": statistiken + }) + elif msg_type == "new_shoe": + self.reset_runden_statistik() + self.anzahl_decks = payload.get("num_decks", self.anzahl_decks) + + elif sender_adresse == self.spieler_addr: + # Nachrichten vom Spieler + if msg_type == "recommendation_request": + spieler_hand_str = payload.get("player_hand", []) + + spieler_hand_werte = [] + for s in spieler_hand_str: + card_obj = _card_from_str_counter(s) + if card_obj: + if card_obj['rank'] in ['J', 'Q', 'K']: + spieler_hand_werte.append(10) + elif card_obj['rank'] == 'A': + spieler_hand_werte.append(11) + else: + spieler_hand_werte.append(int(card_obj['rank'])) + + dealer_up_card_str = payload.get("dealer_up_card") + croupier_offene_karte_wert = 0 + if dealer_up_card_str: + dealer_card_obj = _card_from_str_counter(dealer_up_card_str) + if dealer_card_obj: + if dealer_card_obj['rank'] in ['J', 'Q', 'K']: + croupier_offene_karte_wert = 10 + elif dealer_card_obj['rank'] == 'A': + croupier_offene_karte_wert = 11 + else: + croupier_offene_karte_wert = int(dealer_card_obj['rank']) + + true_count = self.true_count_berechnen() + empfehlung = self.aktion_empfehlen(spieler_hand_werte, croupier_offene_karte_wert, true_count) + + expected_value = 0.0 + + # Sendet die Aktions-Empfehlung zurück an den Spieler + self._send_udp_message(self.spieler_addr[0], self.spieler_addr[1], { + "type": "action_recommendation", + "payload": { + "recommended_action": empfehlung, + "expected_value": expected_value + } + }) + + elif msg_type == "bet_request": + # Spieler fordert eine Einsatzhöhe + true_count = self.true_count_berechnen() + empfohlener_einsatz = self.optimale_einsatzhoehe_empfehlen(true_count) + # Sendet die Einsatzempfehlung zurück an den Spieler + self._send_udp_message(self.spieler_addr[0], self.spieler_addr[1], { + "type": "bet_recommendation", + "payload": { + "recommended_bet": empfohlener_einsatz + } + }) + + elif msg_type == "statistics_request": + # Spieler fordert Statistiken an + statistiken = self.gib_statistiken_an_croupier(payload.get("requester_id", None)) + # Sendet die angeforderten Statistiken zurück an den Spieler + self._send_udp_message(self.spieler_addr[0], self.spieler_addr[1], { + "type": "statistics_response", + "payload": statistiken + }) + + else: + print(f"[Kartenzaehler] Unbekannter Nachrichtentyp '{msg_type}' oder unerwarteter Sender {sender_adresse}") + + def run(self): + """ + Die Hauptschleife des Kartenzählers. Empfängt kontinuierlich Nachrichten + und verarbeitet diese. + """ + self.udp_socket.settimeout(1.0) + while True: + nachricht, sender_adresse = self.empfangen() # Versucht, eine Nachricht zu empfangen + if nachricht: + self.verarbeite_eingehende_nachricht(nachricht, sender_adresse) + time.sleep(0.01) + + +# Maintprogramm-Ausführung +if __name__ == "__main__": + # Prüfen, ob die richtige Anzahl an Kommandozeilenargumenten übergeben wurde + # Erwartet: Skriptname + 6 Argumente für IPs/Ports = 7 Elemente in sys.argv + if len(sys.argv) != 7: + print(f"Nutzung: python Kartenzaehler.py ") + print(f"Beispiel: python Kartenzaehler.py 127.0.0.1 12000 127.0.0.1 12001 127.0.0.1 12002") + sys.exit(1) + + KARTEZAEHLER_LISTEN_IP = sys.argv[1] + KARTEZAEHLER_LISTEN_PORT = int(sys.argv[2]) + CROUPIER_IP = sys.argv[3] + CROUPIER_PORT = int(sys.argv[4]) + SPIELER_IP = sys.argv[5] + SPIELER_PORT = int(sys.argv[6]) + + # Erstelle eine Instanz des Kartenzaehlers mit den übergebenen Einstellungen + kartenzaehler_programm = Kartenzaehler( + KARTEZAEHLER_LISTEN_IP, KARTEZAEHLER_LISTEN_PORT, + CROUPIER_IP, CROUPIER_PORT, + SPIELER_IP, SPIELER_PORT + ) + kartenzaehler_programm.run() # Startet die Hauptschleife des Kartenzählers \ No newline at end of file diff --git a/Uebung5/U5.txt b/Uebung5/U5.txt new file mode 100644 index 0000000..3e8dd22 --- /dev/null +++ b/Uebung5/U5.txt @@ -0,0 +1,135 @@ +Übungsblatt 5 Rechnernetze Nils Böhnke + +Aufgabe1 +Allgemein: +-Man soll mit 2 oder mehr Karten so nahe wie möglich an 21 Punkte kommen ohne diese zu überschreiten +-Bilderkarten wie (König, Dame, Bube) zählen 10 Punkte, Asse entweder 1 oder 11 +-Dealer oder Croupier spielt nach festen Regeln: zieht Karten bis mindestens 17 erreicht wird +-Blackjack schlägt jede andere Kombination mit 21 (Ass + 10) + +Klasse Croupier: +-kartendecks: Liste aller Karten (aus 1-8 vollständigen Decks) +-hand: Liste der Karten in seiner Hand +-spieler_haende: SpielerId -> Hand des Spielers +-ausgegebene_karten: Liste aller bisher gezogenen Karten +-kartenzaehler: Verbindung Referenz zum Kartenzähler + +-erstelle_kartendeck(anzahl_decks): Erstellt n Decks mit allen Karten +-mische_karten(): zufälliges Neuanordnen des Decks +-karte_geben(): Gibt Karte aus dem Deck und speichert sie +-spiel_starten(spieler): Teilt sich selbst uns Spieler Karten aus +-spiel durchfuehren(): Führt Spiel mit Spielern durch +-wert_berechnen(Hand): Punktwert ermitteln +-auswertung(spieler_hand): vergleicht Hand mit eigener +-gewinn_berechnen(einsatz, ergebnis): Berechnet Auszahlung +-statistik_abfragen(): Statistik von Kartenzähler abrufen + + +Klasse Spieler: +-kapital: Aktueller Kontostand +-einsatz: Aktueller Einsatz für Runde +-hand: Aktuelle Kartenhand +-id: SpielerId +-kartenzaehler: Referenz zum Kartenzähler + +-einsatz_berechnen(true_count): Berechnet optimalen Einsatz +-empfehlung_anfragen(Hand, dealer_karte): Fragt beim Zähler nach optimalem Spielzug +-spielzug_ausfuehren(Empfehlung): Führt Aktion aus - Hit, Stand etc. +-karte_erhalten(karte): Fügt Karte zur Hand hinzu +-spielausgang_verarbeiten(Ergebnis, gewinnbetrag): Kapital anpassen +-zuruecksetzten(): Einsatz und Hand nach Runde zurücksetzten + + +Klasse Kartenzähler: +Brainstorming: +-anzahl_decks: Zahl der gespielten Decks +-running_count: Aktuelle Count-Summe nach Hi-Lo +-verwendete_karten: Liste aller gespielten Karten +-statistik: Werte wie blackjacks, gewinne, count_verlauf, gesamtspiele + +-spiel_starten(anzahl_decks): Initialisiert Zähler mit Info vom Croupier +-karte_gesehen(karte): Wertet gezogene Karte für Count aus +-true_count_berechnen(): Berechnet true Count aus Running Count/ geschätzte Rest_Decks +-aktion_empfehlen(Hand, dealer_karte): Gibt optimale Spielentscheidung zurück +-spielausgang_speichern(ergebnis): Statistik aktualisieren +-statistik_geben(): Gibt aktuelle Statistik auf Anfrage zurück + +Umgesetzt: +-self.anzahl_decks: Diese Variable speichert die Gesamtanzahl der Kartendecks (z.B. 6 oder 8), mit denen das aktuelle Spiel gespielt wird +-self.laufender_zaehlwert: Dies ist der "Running Count" des Kartenzählers +-self.anzahl_karten_gespielt: Diese Variable verfolgt die Gesamtanzahl der Karten +-self.historie_karten: Eine Liste, die alle Karten speichert, die in der aktuellen Runde ausgeteilt wurden +-self.statistik_gewinne: Ein Dictionary, das die Anzahl der gewonnenen Runden für jeden Spieler (identifiziert durch seine ID) speichert +-self.statistik_blackjacks: Dictionary, das die Anzahl der Blackjacks für jeden Spieler speichert +-self.statistik_runden_gespielt: Dictionary, das die Gesamtzahl der gespielten Runden für jeden Spieler aufzeichnet +-karten_verarbeiten(self, karten_str_liste): nimmt eine Liste von Karten-Strings entgegen, die vom Croupier übermittelt werden +-true_count_berechnen(self): Diese Funktion berechnet den "True Count" +-aktion_empfehlen(self, spieler_hand_werte, croupier_offene_karte_wert, true_count): empfängt die Werte der Spielerhand +-optimale_einsatzhoehe_empfehlen(self, true_count): Empfehlung für die Höhe des nächsten Einsatzes des Spielers zurück +-statistik_aktualisieren(self, spieler_id, gewonnen, hatte_blackjack): Statistiken für einen Spieler nach jeder Runde zu aktualisieren +-gib_statistiken_an_croupier(self, spieler_id=None): bereitet die gesammelten Statistiken auf und gibt sie zurück, entweder für einen spezifischen Spieler oder für alle Spieler +-reset_runden_statistik(self): Setzt die Zählwerte zurück +-verarbeite_eingehende_nachricht(self, nachricht, sender_adresse): empfängt und leitet eingehende UDP-Nachrichten weiter + + +Aufgabe2: +User Datagram Protocol (UDP) ist verbindungslos +-Zustellung ist nicht garantiert +-Reihenfolge ist nicht garantiert +-Keine automatische Wiederholung bei Verlust +-Keine Absicherung gegen Duplikate + +-Empfänger quittiert Nachricht um Zustellung zu garantieren +-> System zur Quittierung +-Einheitliches Json Format zur klaren Definition von Nachrichten +-Feste IP-Adressen/ Ports + +Anhand von den vergangenen Übungen ist festzuhalten, dass die Kommunikation kompatible sein muss, dafür müssen Abstimmungen über die genaue und einheitliche Kommunikation festgelegt werden, um Nachrichten richtig verarbeiten zu können. Dadurch kann fehlerhafte Kommunikation vermieden werden, indem die Spezifikationen ausgearbeitet und ausgehandelt werden. + + +Aufgabe3: +Ich musste gemäß des ersten Buchstaben vom Nachnamen den Kartenzähler implementieren (siehe Code). +Um die Situation testen zu können, habe ich versucht den Croupier zu entwickeln, grundsätzlich funktionieren die Programme, jedoch entsteht beim Croupier ein WinError 10054. Nach Recherche kann dies mit der Firewall zusammenhängen. + +$ python Croupier.py ***.*.*.* ***** ***.*.*.* ***** ***.*.*.* ***** * +[Croupier] Deck mit 6 Decks gemischt. Gesamt: 312 Karten. +[Croupier] Lauscht auf ***.*.*.* ***** + +============================== +[Croupier] Spiel startet mit 6 Decks. +============================== +[Croupier] -> Gesendet an ***.*.*.* *****: Typ='game_start_info' Payload={'num_decks': 6}... + +--- Runde 1 --- +[Croupier] -> Gesendet an ***.*.*.* *****: Typ='request_bet' Payload={}... +[Croupier] Fehler beim Empfangen vom Spieler: [WinError 10054] Eine vorhandene Verbindung wurde vom Remotehost geschlossen +[Croupier] Spieler hat keinen g▒ltigen Einsatz gesendet oder Timeout. +[Croupier] Spieler hat keinen g▒ltigen Einsatz gemacht oder Spiel beendet. + + +Hier noch die Ausgabe von dem Kartenzähler: + +$ python Kartenzaehler.py ***.*.*.* ***** ***.*.*.* ***** ***.*.*.* ***** +[Kartenz▒hler] Lauscht auf ***.*.*.* ***** +[Kartenz▒hler] <- Empfangen von 127.0.0.1:12001: Typ='game_start_info' Payload={'num_decks': 6} +[Kartenz▒hler] Z▒hlwerte zur▒ckgesetzt. Statistiken bleiben bestehen. +[Kartenz▒hler] Spiel initialisiert: 6 Decks. +Traceback (most recent call last): + File "...", line 475, in + kartenzaehler_programm.run() + File "...", line 449, in run + time.sleep(0.01) + + + +Aufgabe4: +Wir haben als Gruppe zusammengearbeitet und die Aufgabe so entwickelt, dass die Rahmenbedingungen stimmen und die Programm bei der Kommunikation keine Probleme aufweisen. +Dabei war darauf zu achten, dass das Nachrichtensystem einheitlich ist, um eine einheitliche Kommunikation zu garantieren. + +Durch die Vorabsprache sind keine größeren Probleme aufgetreten, nur die Firewall mit dem WinError 10054 hat Probleme bereitet. + + + + + + diff --git a/Uebung6/Aufgabe1/Uebung6_Grafik_Aufgabe1.png b/Uebung6/Aufgabe1/Uebung6_Grafik_Aufgabe1.png new file mode 100644 index 0000000..a3848cc Binary files /dev/null and b/Uebung6/Aufgabe1/Uebung6_Grafik_Aufgabe1.png differ diff --git a/Uebung6/Aufgabe3/Frame1.1.png b/Uebung6/Aufgabe3/Frame1.1.png new file mode 100644 index 0000000..d6c9784 Binary files /dev/null and b/Uebung6/Aufgabe3/Frame1.1.png differ diff --git a/Uebung6/Aufgabe3/Frame1.2.png b/Uebung6/Aufgabe3/Frame1.2.png new file mode 100644 index 0000000..4e6df62 Binary files /dev/null and b/Uebung6/Aufgabe3/Frame1.2.png differ diff --git a/Uebung6/Aufgabe3/Frame1.3.png b/Uebung6/Aufgabe3/Frame1.3.png new file mode 100644 index 0000000..b2c9c0c Binary files /dev/null and b/Uebung6/Aufgabe3/Frame1.3.png differ diff --git a/Uebung6/Aufgabe3/Frame1.4.png b/Uebung6/Aufgabe3/Frame1.4.png new file mode 100644 index 0000000..8dccb74 Binary files /dev/null and b/Uebung6/Aufgabe3/Frame1.4.png differ diff --git a/Uebung6/Aufgabe3/Frame2.1.png b/Uebung6/Aufgabe3/Frame2.1.png new file mode 100644 index 0000000..5b412b2 Binary files /dev/null and b/Uebung6/Aufgabe3/Frame2.1.png differ diff --git a/Uebung6/Aufgabe3/Frame2.2.png b/Uebung6/Aufgabe3/Frame2.2.png new file mode 100644 index 0000000..098f968 Binary files /dev/null and b/Uebung6/Aufgabe3/Frame2.2.png differ diff --git a/Uebung6/Aufgabe3/Frame2.3.png b/Uebung6/Aufgabe3/Frame2.3.png new file mode 100644 index 0000000..c4f4285 Binary files /dev/null and b/Uebung6/Aufgabe3/Frame2.3.png differ diff --git a/Uebung6/Aufgabe3/Frame2.4.png b/Uebung6/Aufgabe3/Frame2.4.png new file mode 100644 index 0000000..fdccb98 Binary files /dev/null and b/Uebung6/Aufgabe3/Frame2.4.png differ diff --git a/Uebung6/Aufgabe3/Frame3.1.png b/Uebung6/Aufgabe3/Frame3.1.png new file mode 100644 index 0000000..2d702e7 Binary files /dev/null and b/Uebung6/Aufgabe3/Frame3.1.png differ diff --git a/Uebung6/Aufgabe3/Frame3.2.png b/Uebung6/Aufgabe3/Frame3.2.png new file mode 100644 index 0000000..287b182 Binary files /dev/null and b/Uebung6/Aufgabe3/Frame3.2.png differ diff --git a/Uebung6/Aufgabe3/Frame3.3.png b/Uebung6/Aufgabe3/Frame3.3.png new file mode 100644 index 0000000..af12adf Binary files /dev/null and b/Uebung6/Aufgabe3/Frame3.3.png differ diff --git a/Uebung6/Aufgabe3/Frame3.4.png b/Uebung6/Aufgabe3/Frame3.4.png new file mode 100644 index 0000000..f779c5b Binary files /dev/null and b/Uebung6/Aufgabe3/Frame3.4.png differ diff --git a/Uebung6/Aufgabe3/Frame4.1.png b/Uebung6/Aufgabe3/Frame4.1.png new file mode 100644 index 0000000..c2e17d4 Binary files /dev/null and b/Uebung6/Aufgabe3/Frame4.1.png differ diff --git a/Uebung6/Aufgabe3/Frame4.2.png b/Uebung6/Aufgabe3/Frame4.2.png new file mode 100644 index 0000000..c6cff3a Binary files /dev/null and b/Uebung6/Aufgabe3/Frame4.2.png differ diff --git a/Uebung6/Aufgabe3/Frame4.3.png b/Uebung6/Aufgabe3/Frame4.3.png new file mode 100644 index 0000000..40da60e Binary files /dev/null and b/Uebung6/Aufgabe3/Frame4.3.png differ diff --git a/Uebung6/Aufgabe3/Frame4.4.png b/Uebung6/Aufgabe3/Frame4.4.png new file mode 100644 index 0000000..da7174e Binary files /dev/null and b/Uebung6/Aufgabe3/Frame4.4.png differ diff --git a/Uebung6/Aufgabe3/Uebung6_Aufgabe3.pcapng b/Uebung6/Aufgabe3/Uebung6_Aufgabe3.pcapng new file mode 100644 index 0000000..498a1ff Binary files /dev/null and b/Uebung6/Aufgabe3/Uebung6_Aufgabe3.pcapng differ diff --git a/Uebung6/Aufgabe4/2025S_Ubung_Rechnernetze_6_Aufgabe4.pdf b/Uebung6/Aufgabe4/2025S_Ubung_Rechnernetze_6_Aufgabe4.pdf new file mode 100644 index 0000000..4fbf470 Binary files /dev/null and b/Uebung6/Aufgabe4/2025S_Ubung_Rechnernetze_6_Aufgabe4.pdf differ diff --git a/Uebung6/U6.txt b/Uebung6/U6.txt new file mode 100644 index 0000000..a13e9c6 --- /dev/null +++ b/Uebung6/U6.txt @@ -0,0 +1,150 @@ +Übungsblatt 6 Rechnernetze Nils Böhnke + +Aufgabe1 +1. Sliding-Window +-Wird zur Fehler- und Flusskontrolle eingesetzt +-Ermöglicht einem Sender mehrere Datenpakete (Segmente) zu versenden ohne auf eine Bestätigung (ACK) von jedem einzelnen Pakets zu warten +-Wie ein Fenster was sich über einen Strom von Daten bewegt +->Sendefenster: Bereich von Sequenznummer die der Sender senden darf, umfasst Pakete welche bereits gesendet aber noch nicht bestätigt wurde, und Pakete welche gesendet werden können +->Fenstergröße: Größe definiert maximale Anzahl von Paketen die Sender unbestätigt gleichzeitig senden darf +->Empfangsfenster: Bereich von Sequenznummer die Empfänger akzeptiert und erwartet +-Wenn Sender Paket sendet, rückt Sendefenster vor +-Wenn Empfänger Paket empfängt und bestätigt rückt Empfangsfenster auf seiner Seite vor +-Empfang der Bestätigung (ACK) rückt Sendefenster weiter +-Geht Paket verloren Timeout, Sender sendet Paket erneut +Wofür: +-Für Effizienz von Netzwerkprotokollen +-Ohne müsste Sender nach jedem Paket auf Bestätigung warten -> hohe Latenz, ineffiziente Auslastung der Bandbreite +-> Flusskontrolle und Fehlerkontrolle + +2.TCP Tahoe +-TCP-Kongestionskontrollalgorithmus: vermeidet Überlastung im Netzwerk zu vermeiden +-Effizienten Datentransport zu gewährleisten +-Langsamer Start: nach dem Verbindungsaufbau oder Timeout beginnt Überlastungsfenster sehr klein; für jedes empfangene ACK wird Überlastungsfenster um 1 MSS erhöht; Ziel Bandbreite zu finden +-Überlastungsvermeidung: wenn Schwelle erreicht Wechsel Langsamer Start -> Überlastungsvermeidung; Überlastungsfenster wächst bei jeder Runde von gesendeten Paketen +-Schnelle Neuübertragung: wenn Sender drei doppelte ACK für selbes Segment empfängt nimmt Sender an, das Segment verloren gegangen ist und sofort überträgt ohne auf Timeout zu warten +Wofür: +-Sorgt für eine effiziente Nutzung verfügbarer Bandbreite, aber auch auf Überlastung zu reagieren +-Konservative Reaktion auf Paketverlust durch Rückkehr zu Langsamer Start um Netzwerk nicht zu überlasten + +3.TCP Reno: +-Weiterentwicklung von TCP Tahoe -> Leistung bei Paketverlusten zu verbessern +-Schnelle Wiederherstellung: Wird ausgelöst, wenn drei doppelte ACKs empfangen werden +wofür: +-Verbessert Leistung von TCP + +4.TCP Vegas: +-Ist ein präventiver Algorithmus +-Versucht Überlastung zu vermeiden in dem es Netzwerkauslastung mit Messung analysiert +-Beobachtet Round Trip Time (RTT), wenn diese steigt versucht der Algorithmus das zu erkennen und gegenzusteuern, sodass die Warteschlange von Paketen nicht geflutet wird +-Misst minimale RTT und aktuelle RTT und berechnet Unterschied +-Fensteranpassung basiert auf der Differenz +-Weniger Paketverluste und höherer Durchsatz +Wofür: +-Der RTT Algorithmus zur Überlastungssteuerung um Paketverluste zu vermeiden + + +Liste: +-Schicht 7: Anwendungsschicht (Application Layer) +->HTTP (Hypertext Transfer Protocol) HTTP wird von Webbrowsern und Webservern verwendet, um Hypertext-Dokumente und andere Webinhalte auszutauschen +->FTP (File Transfer Protocol) FTP wird zum Übertragen von Dateien zwischen Clients und Servern verwendet +->SMTP (Simple Mail Transfer Protocol) SMTP ist das Standardprotokoll für den Versand von E-Mails +->POP3 (Post Office Protocol version 3) IMAP ist ein weiteres Protokoll zum Abrufen von E-Mails +->DNS (Domain Name System) DNS übersetzt menschenlesbare Domainnamen in numerische IP-Adressen +->SSH (Secure Shell) SSH ermöglicht eine sichere Fernverwaltung von Computern und sichere Dateiübertragungen +->DHCP (Dynamic Host Configuration Protocol) Hosts dynamisch IP-Adressen und andere Netzwerkkonfigurationsparameter zuzuweisen +-Schicht 6: Darstellungsschicht (Presentation Layer) +->TLS (Transport Layer Security) / SSL (Secure Sockets Layer) TLS/SSL sorgen für die Verschlüsselung, Authentifizierung und Datenintegrität von Daten, die über eine Netzwerkschicht gesendet werden +-Schicht 5: Sitzungsschicht (Session Layer) +->ADSP (AppleTalk Data Stream Protocol) verbindungsorientiertes Protokoll im AppleTalk-Stack, das Ende-zu-Ende-Byte-Stream-Dienste mit Flusskontrolle bietet +-Schicht 4: Transportschicht (Transport Layer) +->TCP (Transmission Control Protocol) TCP bietet eine zuverlässige, verbindungsorientierte und strombasierte Datenübertragung zwischen Anwendungen auf verschiedenen Hosts +->UDP (User Datagram Protocol) UDP bietet eine unzuverlässige, verbindunglose Datagramm-Übertragung +-Schicht 3: Vermittlungsschicht (Network Layer) +->IP (Internet Protocol) verantwortlich für die logische Adressierung (IP-Adressen) und das Routing von Paketen +->ICMP (Internet Control Message Protocol) von Routern und Hosts verwendet, um Fehlerberichte und Kontrollnachrichten über das Netzwerk zu senden +->IGMP (Internet Group Management Protocol) Mitgliedschaft von Hosts in Multicast-Gruppen zu verwalten +-Schicht 2: Sicherungsschicht (Data Link Layer) +->ARP (Address Resolution Protocol) ogische IP-Adresse in eine physikalische MAC-Adresse auf derselben lokalen Netzwerksegment zu übersetzen +-> Ethernet definiert sowohl das Format der Datenframes als auch die physikalischen Eigenschaften für die Übertragung von Daten +-Schicht 1: Bitübertragungsschicht (Physical Layer) +->WLAN (Wireless Local Area Network) drahtlose Kommunikation + +Aufgabe2: +a) nmap -sn 192.168.1.0/24 +NMAP führt einen Ping-Scan durch das Ergebnis sind, wie viele Hosts online sind, diese werden anpingt, bei mir sind es 0 Hosts. Nmap done: 256 IP addresses (0 hosts up) scanned in 247.91 seconds + +b) nmap -O scanme.nmap.org +Das Betriebssystem ist Linux 4.X|5.X. + +c) whois nmap.org +18. Januar 1999 + +d) nmap -sS -T4 -p 1-1000 192.168.1.0/24 +-sS: SYN-Scan (schnell & unauffällig), T4: Timing-Modus („Aggressiv“, aber stabil),-p 1-1000: Scan der ersten 1000 TCP-Ports, 192.168.1.0/24: Zielnetz + +e) Nmap sendet ein Syn-Paket, die Reaktion: SYN-ACK-> Port offen, RST->Port geschlossen +Nmap sendet kein ACK zurück, bricht die Verbindung ab -> Half open +Ist weniger auffällig und Schneller, Ideal für Port-Erkennung + +f) Durch den überwiegenden Scan von Internetseiten ist Port 80 und 443 angegeben. + + +Aufgabe3: + +DHCP wird benutzt, um einem Gerät eine IP-Adresse zuzuweisen, um diese zu erzeugen kann man beispielsweise die Netzwerkverbindung zurücksetzten, da um ein DHCP-Paket zu empfangen oder senden ein Client eine DHCP-Anfrage zu stellen. + + +Nachdem ich mit ipconfig /release und ipconfig /renew einen neuen Handshake erzwungen habe, wurden mir vier Anfragen aufgezeigt. + + +Hier ein paar Beobachtungen: +Die Source address ist 0.0.0.0 und die Anfrage wird an Destination address 255.255.255.255 gesendet. + +Pakete über UDP gesendet Client Port ist 67 und Server-Port 68, Identifikation über Mac-Adresse. + +UDP- und IPV4-Header. + +Option: (12) Host Name enthält den Namen des Hosts. + +Die anderen Frames spiegeln im Prinzip das gleiche, aus diesem Grund wird nicht jeder beschrieben. + + +Aufgabe4: + +Betrachtet man die Initialisierung, so lässt sich feststellen, dass für jeden Knoten eine Tabelle angelegt wird. In den Zeilen kann man zu einem Knoten ablesen, in den Spalten via, also über einen Knoten. Im Graphen und in der Tabelle sind die Kosten, also Kantengweichte zu den jeweils benachbarten Routern eingetragen. + +Schaut man sich nun Router A in der Initialisierung an, ist ein benachbarter Knoten B mit Kantengweicht von A zu B mit 3 und der andere benachbarte Knoten von A zu C mit Kantengewicht mit 23. + +Bei der Aktualisierung werden nun Pfade erweitert. Man kann nu andere Wege zu den bekannten Knoten untersuchen und die Kosten mittels der Addition berechnen. So kann man von A nach C über B beispielsweise gelangen und die Kosten belaufen sich auf 5. Des Weiteren wird über C D erschlossen. + +Dies wird solange wiederholt, bis alle vorgesehenen Kästchen ausgefüllt und die niedrigsten Werte bestimmt sind. + +Bei den anderen Routern passiert das Gleiche. + + +Algorithmus: + +1) Erstelle für jeden Knoten im Graphen eine Tabelle +-> Erstelle für jeden anderen Knoten der nicht der Hauptknoten der Tabelle ist eine Spalte für via Knoten +-> Erstelle für jeden anderen Knoten in der jeweiligen Knotentabelle eine Zeile + +2) Eintragen von allen direkten Distanzen + +3) Tabelle erweitern bis alle Plätze voll sind, Kantengewichte addieren + +4) Den Prozess beenden, wenn alle Plätze voll sind, alle optimalen Distanzen sind berechnet + + +a) & b) in PDF + +c) D sendet keine Updates mehr, keine Antwort + Nach einer gewissen Zeit wird D als nicht mehr erreichbar markiert, Timeout + Router aktualisieren ihre Tabellen und senden neue Updates, andere Router erfahren davon, Route wird entfernt + Router können betroffene Routen als unendlich markieren + + + + + +