import pyxel
import random

# ══════════════════════════════════════════════════════════
#  KONSTANTEN
# ══════════════════════════════════════════════════════════
SCREEN_W  = 345
SCREEN_H  = 272
CELL      = 16
GRID_SIZE = 10

LEFT_X  = 4
RIGHT_X = LEFT_X + GRID_SIZE * CELL + 16
GRID_Y  = 28

# Farben
BLACK      = 0
DARK_BLUE  = 1
PURPLE     = 2
DARK_GRN   = 3
BROWN      = 4
DARK_GRAY  = 5
LIGHT_GRAY = 6
WHITE      = 7
RED        = 8
ORANGE     = 9
YELLOW     = 10
GREEN      = 11
BLUE       = 12
CYAN       = 12
INDIGO     = 13
PINK       = 14
PEACH      = 15

COL_BG       = DARK_BLUE
COL_GRID     = INDIGO
COL_TEXT     = WHITE
COL_DIM      = DARK_GRAY
COL_CURSOR   = YELLOW
COL_PREVIEW  = GREEN
COL_CONFLICT = RED
COL_HIT      = RED
COL_SUNK     = ORANGE

# Zellzustände
EMPTY   = 0
SHIP_OK = 1
MISS    = 2
HIT     = 3
SUNK    = 4

# Waffenmodi
MODE_NORMAL  = 0
MODE_BOMB    = 1
MODE_TORPEDO = 2

# Spielphasen
PHASE_PLACE   = "place"
PHASE_HANDOFF = "handoff"
PHASE_BATTLE  = "battle"
PHASE_RESULT  = "result"
PHASE_TORPEDO = "torpedo_anim"
PHASE_EXPLODE = "explode"
PHASE_OVER    = "over"
PHASE_TITLE  = "title"

# Spielmodus
MODE_SP = "singleplayer"
MODE_MP = "multiplayer"

# KI-Stufen
AI_EASY   = 0
AI_MEDIUM = 1
AI_HARD   = 2

# Charakter-Beschreibungen für Intro
AI_TITLES = ["Matrose", "Kapitaen", "Admiral"]
AI_FULLNAMES = ["Matrose Max", "Kapitaen Klink", "Admiral Graubart"]
AI_QUOTES = [
    "\"Ich schiess einfach mal drauflos!\"",
    "\"Mit System gewinnt man Kriege.\"",
    "\"Ich rieche deine Schiffe foermlich.\"",
]
AI_DIFFICULTY_LABEL = ["Leicht", "Mittel", "Schwer"]

# Hard-KI Cheat-Quote (Chance pro Schuss ein bekanntes Schiffsfeld zu waehlen,
# nur wenn kein aktiver Hunt-Modus laeuft)
AI_HARD_CHEAT_CHANCE = 0.85

PHASE_AI_INTRO = "ai_intro"

# KI-Reaktionstexte
AI_REACTIONS_HIT  = ["TREFFER!", "Erwischt!", "Volltreffer!"]
AI_REACTIONS_MISS = ["Verfehlt.", "Wasser...", "Daneben!"]
AI_REACTIONS_SUNK = ["Versenkt! Haha!", "Eines weniger!", "Buh!"]

# KI Timing
AI_THINK_DELAY = 55   # Frames bis KI schiesst
AI_REACTION_DUR = 110  # Frames Reaktionstext sichtbar

SHIPS = [5, 4, 3, 3, 2]

KEY_INITIAL_DELAY = 20
KEY_REPEAT_RATE   = 4
KEY_REVEAL        = pyxel.KEY_A

TORP_SPEED  = 3
EXPLODE_DUR = 45

# ══════════════════════════════════════════════════════════
#  SPRITES
# ══════════════════════════════════════════════════════════

def load_sprites():
    im = pyxel.images[1]

    # ── Wasser – 3 Frames
    im.set(0, 0, [
        "1111166666cccc66",
        "1116666ccccccc6c",
        "116cccccccccc6cc",
        "66cccccccc6ccc66",
        "6cccccc6ccc66611",
        "ccccc6cc66661111",
        "cccc6c6666111166",
        "ccc6c666111166cc",
        "cc6c6661116666cc",
        "c6c6666116666ccc",
        "66666661166ccccc",
        "6666661166cccccc",
        "666661166ccccccc",
        "66661166cccccc6c",
        "6661166ccccccc66",
        "661166cccccc6666",
    ])
    im.set(16, 0, [
        "cc666611116666cc",
        "6666661116666ccc",
        "666661166ccccccc",
        "66661166cccccccc",
        "6661166ccccccccc",
        "111166cccccccccc",
        "11166ccccccccccc",
        "1166cccccccccc6c",
        "166ccccccccccc66",
        "66cccccccccc6666",
        "6ccccccccc666666",
        "cccccccccc66cccc",
        "cccccccc66cccccc",
        "cccccc6666cccccc",
        "ccccc66666cccccc",
        "cccc666666cccccc",
    ])
    im.set(32, 0, [
        "1166cccccccc6666",
        "166ccccccccc6666",
        "66cccccccc666611",
        "6ccccccc66661111",
        "cccccc6666111166",
        "ccccc66661111666",
        "cccc666611116666",
        "ccc6666111166ccc",
        "cc66661116666ccc",
        "c6666111666666cc",
        "66661116666ccccc",
        "6661166ccccccccc",
        "661166cccccccccc",
        "61166ccccccccccc",
        "1166cccccccccccc",
        "166ccccccccccccc",
    ])

    # ── Explosionen – 5 Frames
    # Frame 0 – Treffer X (rot)
    im.set(0, 16, [
        "8800000000000088",
        "8880000000000888",
        "0888000000008880",
        "0088800000088800",
        "0008888000888000",
        "0000888888880000",
        "0000088888800000",
        "0000008888000000",
        "0000008888000000",
        "0000088888800000",
        "0000888888880000",
        "0008888000888000",
        "0088800000088800",
        "0888000000008880",
        "8880000000000888",
        "8800000000000088",
    ])
    # Frame 1 – klein orange
    im.set(16, 16, [
        "0000000000000000",
        "0000000000000000",
        "0000009999000000",
        "0000099999900000",
        "0000999999990000",
        "0009999999999000",
        "0009999999999000",
        "0009999999999000",
        "0009999999999000",
        "0009999999999000",
        "0000999999990000",
        "0000099999900000",
        "0000009999000000",
        "0000000000000000",
        "0000000000000000",
        "0000000000000000",
    ])
    # Frame 2 – mittel gelb-orange
    im.set(32, 16, [
        "0000000900000000",
        "0000009999000000",
        "0000099999900000",
        "0000999999990000",
        "0009999999999000",
        "0099999999999900",
        "0999999999999990",
        "9999999999999999",
        "9999999999999999",
        "0999999999999990",
        "0099999999999900",
        "0009999999999000",
        "0000999999990000",
        "0000099999900000",
        "0000009999000000",
        "0000000900000000",
    ])
    # Frame 3 – gross gelb
    im.set(48, 16, [
        "0a00000000000a00",
        "a0aaaa0000aaaa0a",
        "0aaaaaa00aaaaaa0",
        "0aaaaaaaaaaaaa00",
        "aaaaaaaaaaaaaa00",
        "aaaaaaaaaaaaaa00",
        "aaaaaaaaaaaaaa00",
        "aaaaaaaaaaaaaa00",
        "aaaaaaaaaaaaaa00",
        "aaaaaaaaaaaaaa00",
        "aaaaaaaaaaaaaa00",
        "0aaaaaaaaaaaaa00",
        "0aaaaaa00aaaaaa0",
        "a0aaaa0000aaaa0a",
        "0a00000000000a00",
        "0000000000000000",
    ])
    # Frame 4 – verblassend rot
    im.set(64, 16, [
        "8000000000000008",
        "0800000000000080",
        "0080000000000800",
        "0008000000008000",
        "0000800000080000",
        "0000080000800000",
        "0000008008000000",
        "0000000000000000",
        "0000000000000000",
        "0000008008000000",
        "0000080000800000",
        "0000800000080000",
        "0008000000008000",
        "0080000000000800",
        "0800000000000080",
        "8000000000000008",
    ])

    # ── Torpedo horizontal
    im.set(0, 32, [
        "0000000000000000",
        "0000000000000000",
        "000000aaa9000000",
        "0000aaaa99000000",
        "00aaaaaa990000000",
        "9999999aa9000000",
        "9999999aa9000000",
        "00aaaaaa990000000",
        "0000aaaa99000000",
        "000000aaa9000000",
        "0000000000000000",
        "0000000000000000",
        "0000000000000000",
        "0000000000000000",
        "0000000000000000",
        "0000000000000000",
    ])
    # ── Torpedo vertikal
    im.set(16, 32, [
        "0000000000000000",
        "0000000aa0000000",
        "000000a99a000000",
        "00000aa99aa00000",
        "0000099999900000",
        "0000099999900000",
        "0000099999900000",
        "0000099999900000",
        "0000099999900000",
        "0000099999900000",
        "0000009999000000",
        "0000000990000000",
        "0000000900000000",
        "0000000000000000",
        "0000000000000000",
        "0000000000000000",
    ])
    # ── Torpedo-Spur horizontal
    im.set(32, 32, [
        "0000000000000000",
        "6000000000000000",
        "0060000000000000",
        "0006000000000000",
        "0000600000000000",
        "0000060000000000",
        "0000006000000000",
        "0000000600000000",
        "0000000060000000",
        "0000000006000000",
        "0000000000600000",
        "0000000000060000",
        "0000000000006000",
        "0000000000000600",
        "0000000000000060",
        "0000000000000006",
    ])
    # ── Torpedo-Spur vertikal
    im.set(48, 32, [
        "0060000000600000",
        "0000000000000000",
        "0006000000060000",
        "0000000000000000",
        "0000600000006000",
        "0000000000000000",
        "0000060000000600",
        "0000000000000000",
        "0000006000000060",
        "0000000000000000",
        "0000000600000006",
        "0000000000000000",
        "0000000060000000",
        "0000000000000000",
        "0000000006000000",
        "0000000000000000",
    ])
    # ── Miss-Splash
    im.set(64, 32, [
        "0000000000000000",
        "0000006000000000",
        "0000666000000000",
        "0006666600000000",
        "006666cc66000000",
        "006cc6cc6c600000",
        "0006cc6cc66000000",
        "00006c6c660000000",
        "0000066666000000",
        "0000006660000000",
        "0000000600000000",
        "0000000000000000",
        "0000000000000000",
        "0000000000000000",
        "0000000000000000",
        "0000000000000000",
    ])
    # ── SUNK / WRACK
    im.set(80, 32, [
        "4445444454444544",
        "4544494444944445",
        "5449444944449444",
        "4498944494498944",
        "498a89498998a894",
        "4498948a98998944",
        "444949a949449444",
        "5444949494444945",
        "4494444944944444",
        "4989449894494984",
        "98a8998a998898a9",
        "498948a9849a8984",
        "44949a9499a94944",
        "4449894989498944",
        "5444944494449445",
        "4544445444544454"
    ])
    # ── Portrait: Kapitaen Klink (Mittel) – 32x32
    im.set(32, 48, [
        "00000999999999900000000000000000",
        "00009999999999990000000000000000",
        "00099944444444999000000000000000",
        "00994444444444449900000000000000",
        "09444444444444444490000000000000",
        "09444444444444444490000000000000",
        "09444488ff8888ff4444900000000000",
        "09444444444444444449000000000000",
        "09444444444444444490000000000000",
        "09444444477774444490000000000000",
        "00944444444444444900000000000000",
        "00099444444444449900000000000000",
        "00009944999944990000000000000000",
        "00000999999999990000000000000000",
        "00000999cc99cc999000000000000000",
        "00009999999999999900000000000000",
        "00099991111199999900000000000000",
        "00099991111199999900000000000000",
        "00009999999999999900000000000000",
        "00000999999999999000000000000000",
        "00000099999999990000000000000000",
        "00000009999999900000000000000000",
        "00000000999999000000000000000000",
        "00000000012211000000000000000000",
        "00000000111111000000000000000000",
        "00000000000000000000000000000000",
        "00000000000000000000000000000000",
        "00000000000000000000000000000000",
        "00000000000000000000000000000000",
        "00000000000000000000000000000000",
        "00000000000000000000000000000000",
        "00000000000000000000000000000000",
    ])

    # ── Portrait: Admiral Graubart (Schwer) – 32x32
    im.set(64, 48, [
        "00001010101010100000000000000000",
        "00010101010101011000000000000000",
        "00115555555555551100000000000000",
        "01155555555555555110000000000000",
        "01555555555555555510000000000000",
        "01555555555555555510000000000000",
        "01555588ff8888ff5555100000000000",
        "01555555555555555510000000000000",
        "01555555555555555510000000000000",
        "01555555599995555510000000000000",
        "00155556666666555100000000000000",
        "00015556666666655100000000000000",
        "00001566666666651000000000000000",
        "00000566666666650000000000000000",
        "00000666666666660000000000000000",
        "00006666666666666000000000000000",
        "00066666666666666600000000000000",
        "00666666666666666660000000000000",
        "06666666666666666666000000000000",
        "06666666666666666666000000000000",
        "00666666666666666660000000000000",
        "00066666666666666600000000000000",
        "00006666666666666000000000000000",
        "00000812222222180000000000000000",
        "00000111111111100000000000000000",
        "00000000000000000000000000000000",
        "00000000000000000000000000000000",
        "00000000000000000000000000000000",
        "00000000000000000000000000000000",
        "00000000000000000000000000000000",
        "00000000000000000000000000000000",
        "00000000000000000000000000000000",
    ])

# ══════════════════════════════════════════════════════════
#  SOUNDEFFEKTE
# ══════════════════════════════════════════════════════════
def setup_sounds():
    # 0: Schiff platziert
    pyxel.sounds[0].set_notes("c3 e3")
    pyxel.sounds[0].set_tones("s")
    pyxel.sounds[0].set_volumes("6")
    pyxel.sounds[0].set_effects("n")
    pyxel.sounds[0].speed = 8

    # 1: Wasser / Miss
    pyxel.sounds[1].set_notes("a3 g3")
    pyxel.sounds[1].set_tones("s")
    pyxel.sounds[1].set_volumes("6")
    pyxel.sounds[1].set_effects("f")
    pyxel.sounds[1].speed = 7

    # 2: Treffer
    pyxel.sounds[2].set_notes("c4 a3 f3")
    pyxel.sounds[2].set_tones("s")
    pyxel.sounds[2].set_volumes("7")
    pyxel.sounds[2].set_effects("f")
    pyxel.sounds[2].speed = 5

    # 3: Versunken
    pyxel.sounds[3].set_notes("c4 a3 f3 d3")
    pyxel.sounds[3].set_tones("s")
    pyxel.sounds[3].set_volumes("7")
    pyxel.sounds[3].set_effects("f")
    pyxel.sounds[3].speed = 7

    # 4: Sieg
    pyxel.sounds[4].set_notes("c3 e3 g3 c3")
    pyxel.sounds[4].set_tones("s")
    pyxel.sounds[4].set_volumes("7")
    pyxel.sounds[4].set_effects("n")
    pyxel.sounds[4].speed = 10

    # 5: Ungültige Platzierung
    pyxel.sounds[5].set_notes("b2 a2")
    pyxel.sounds[5].set_tones("p")
    pyxel.sounds[5].set_volumes("5")
    pyxel.sounds[5].set_effects("n")
    pyxel.sounds[5].speed = 6

# ══════════════════════════════════════════════════════════
#  KEY-REPEAT
# ══════════════════════════════════════════════════════════
_key_held = {}

def key_active(key):
    if pyxel.btn(key):
        _key_held[key] = _key_held.get(key, 0) + 1
        held = _key_held[key]
        if held == 1:
            return True
        if held > KEY_INITIAL_DELAY and (held - KEY_INITIAL_DELAY) % KEY_REPEAT_RATE == 0:
            return True
        return False
    else:
        _key_held[key] = 0
        return False

# ══════════════════════════════════════════════════════════
#  HILFSFUNKTIONEN
# ══════════════════════════════════════════════════════════

def make_grid():
    return [[EMPTY] * GRID_SIZE for _ in range(GRID_SIZE)]

def ship_cells(row, col, length, horiz):
    return [(row + (0 if horiz else i), col + (i if horiz else 0))
            for i in range(length)]

def can_place(grid, cells):
    cell_set = set(cells)
    for r, c in cells:
        if not (0 <= r < GRID_SIZE and 0 <= c < GRID_SIZE):
            return False
        if grid[r][c] != EMPTY:
            return False
        for dr, dc in [(-1,0),(1,0),(0,-1),(0,1)]:
            nr, nc = r+dr, c+dc
            if (nr, nc) in cell_set:
                continue
            if 0 <= nr < GRID_SIZE and 0 <= nc < GRID_SIZE:
                if grid[nr][nc] == SHIP_OK:
                    return False
    return True

def place_ship(grid, cells):
    for r, c in cells:
        grid[r][c] = SHIP_OK

def all_sunk(grid):
    return not any(grid[r][c] == SHIP_OK
                   for r in range(GRID_SIZE)
                   for c in range(GRID_SIZE))
 
def ship_hit_percent(player_state):
    total = sum(len(s) for s in player_state.ships)
    if total == 0:
        return 0.0
    hit = sum(
        1 for s in player_state.ships
        for (r, c) in s
        if player_state.grid[r][c] in (HIT, SUNK)
    )
    return hit / total

def check_and_mark_sunk(grid, ships):
    for ship in ships:
        if all(grid[r][c] == HIT for r, c in ship):
            for r, c in ship:
                grid[r][c] = SUNK
            pyxel.play(3, 3, loop=False)

# ══════════════════════════════════════════════════════════
#  SPIELSTAND
# ══════════════════════════════════════════════════════════

class AIPlayer:
    def __init__(self, level):
        self.level        = level
        self.hunt_targets = []   # Felder zum Absuchen nach Treffer
        self.last_hit     = None
        self.hunt_dir     = None # aktuelle Suchrichtung
        self.tried_dirs   = []
        self.prob_map     = [[0]*GRID_SIZE for _ in range(GRID_SIZE)]

    def reset(self):
        self.hunt_targets = []
        self.last_hit     = None
        self.hunt_dir     = None
        self.tried_dirs   = []
        self.prob_map     = [[0]*GRID_SIZE for _ in range(GRID_SIZE)]

    # ── Wahrscheinlichkeitskarte berechnen (Hard)
    def calc_prob_map(self, grid, remaining_ship_lengths):
        pm = [[0]*GRID_SIZE for _ in range(GRID_SIZE)]
        for length in remaining_ship_lengths:
            for r in range(GRID_SIZE):
                for c in range(GRID_SIZE):
                    # Horizontal
                    if c + length <= GRID_SIZE:
                        cells = [(r, c+i) for i in range(length)]
                        if all(grid[rr][cc] in (EMPTY, SHIP_OK) for rr, cc in cells):
                            for rr, cc in cells:
                                pm[rr][cc] += 1
                    # Vertikal
                    if r + length <= GRID_SIZE:
                        cells = [(r+i, c) for i in range(length)]
                        if all(grid[rr][cc] in (EMPTY, SHIP_OK) for rr, cc in cells):
                            for rr, cc in cells:
                                pm[rr][cc] += 1
        self.prob_map = pm
        return pm

    def remaining_lengths(self, grid, ships):
        result = []
        for ship in ships:
            if any(grid[r][c] == SHIP_OK for r, c in ship):
                result.append(len(ship))
        return result

    # ── Nächsten Schuss wählen
    def choose_shot(self, grid, ships, player_ships=None):
        def shootable(r, c):
            return grid[r][c] in (EMPTY, SHIP_OK)

        if self.level == AI_EASY:
            return self._random_shot(grid)

        elif self.level == AI_MEDIUM:
            return self._hunt_target_shot(grid, shootable)

        else:  # AI_HARD
            # Aktiver Hunt-Modus hat immer Vorrang (fair – jeder gute Spieler macht das)
            if self.hunt_targets:
                return self._hunt_target_shot(grid, shootable)

            # "Bauchgefühl" – mit Wahrscheinlichkeit ein echtes Schiffsfeld treffen
            if player_ships and random.random() < AI_HARD_CHEAT_CHANCE:
                candidates = [
                    (r, c) for ship in player_ships for (r, c) in ship
                    if shootable(r, c)
                ]
                if candidates:
                    return random.choice(candidates)

            # Sonst normale Wahrscheinlichkeitsberechnung
            remaining = self.remaining_lengths(grid, ships)
            pm = self.calc_prob_map(grid, remaining)
            best_val = -1
            best = []
            for r in range(GRID_SIZE):
                for c in range(GRID_SIZE):
                    if shootable(r, c):
                        if pm[r][c] > best_val:
                            best_val = pm[r][c]
                            best = [(r, c)]
                        elif pm[r][c] == best_val:
                            best.append((r, c))
            if best:
                return random.choice(best)
            return self._random_shot(grid)

    def _random_shot(self, grid):
        choices = [(r, c) for r in range(GRID_SIZE)
                          for c in range(GRID_SIZE)
                          if grid[r][c] in (EMPTY, SHIP_OK)]
        return random.choice(choices) if choices else (0, 0)

    def _hunt_target_shot(self, grid, shootable):
        # Filtere ungültige Targets raus
        self.hunt_targets = [(r, c) for r, c in self.hunt_targets if shootable(r, c)]
        if self.hunt_targets:
            return self.hunt_targets.pop(0)
        return self._random_shot(grid)

    def notify_result(self, r, c, was_hit, was_sunk, grid):
        if was_sunk:
            # Aufräumen – Schiff fertig
            self.hunt_targets = []
            self.last_hit     = None
            self.hunt_dir     = None
            self.tried_dirs   = []
        elif was_hit:
            if self.level == AI_EASY:
                return
            if self.last_hit is None:
                # Erster Treffer – alle 4 Richtungen als Targets
                self.last_hit = (r, c)
                self.hunt_dir = None
                self._add_neighbors(r, c, grid)
            else:
                # Zweiter Treffer – Richtung bestimmen und verlängern
                lr, lc = self.last_hit
                dr = r - lr
                dc = c - lc
                # Neue Richtung ermitteln
                if dr != 0:
                    self.hunt_dir = (1, 0) if dr > 0 else (-1, 0)
                elif dc != 0:
                    self.hunt_dir = (0, 1) if dc > 0 else (0, -1)
                # In Richtung weiter und Gegenrichtung
                self.hunt_targets = []
                for sign in [1, -1]:
                    nr, nc = r + self.hunt_dir[0]*sign, c + self.hunt_dir[1]*sign
                    if 0 <= nr < GRID_SIZE and 0 <= nc < GRID_SIZE and grid[nr][nc] in (EMPTY, SHIP_OK):
                        self.hunt_targets.append((nr, nc))
                self.last_hit = (r, c)

    def _add_neighbors(self, r, c, grid):
        for dr, dc in [(-1,0),(1,0),(0,-1),(0,1)]:
            nr, nc = r+dr, c+dc
            if 0 <= nr < GRID_SIZE and 0 <= nc < GRID_SIZE:
                if grid[nr][nc] in (EMPTY, SHIP_OK):
                    if (nr, nc) not in self.hunt_targets:
                        self.hunt_targets.append((nr, nc))

    # ── Schiffe automatisch platzieren
    def auto_place(self, grid, ship_lengths):
        result = []
        for length in ship_lengths:
            placed   = False
            attempts = 0
            while not placed and attempts < 1000:
                attempts += 1
                horiz = random.choice([True, False])
                r = random.randint(0, GRID_SIZE - 1)
                c = random.randint(0, GRID_SIZE - 1)
                cells = ship_cells(r, c, length, horiz)
                if can_place(grid, cells):
                    place_ship(grid, cells)
                    result.append(cells)
                    placed = True
        return result

    # ── Waffe wählen (nur Medium/Hard)
    def choose_weapon(self, p_state, opp_grid):
        if self.level == AI_EASY:
            return MODE_NORMAL, None

        if self.level == AI_MEDIUM:
            if p_state.torpedos > 0 and self.hunt_dir and self.last_hit:
                return MODE_TORPEDO, self.hunt_dir[0] == 0
            if p_state.bombs > 0 and random.random() < 0.15:
                return MODE_BOMB, None
            return MODE_NORMAL, None

        else:  # AI_HARD
            # Torpedo entlang erkannter Schiffslinie
            if p_state.torpedos > 0 and self.hunt_dir and self.last_hit:
                return MODE_TORPEDO, self.hunt_dir[0] == 0
            # Bombe gezielt auf Hotspot, grosszügiger als Medium
            if p_state.bombs > 0 and len(self.hunt_targets) == 0:
                best = 0
                for r in range(GRID_SIZE):
                    for c in range(GRID_SIZE):
                        if self.prob_map[r][c] > best:
                            best = self.prob_map[r][c]
                if best > 5:
                    return MODE_BOMB, None
            return MODE_NORMAL, None

class PlayerState:
    def __init__(self):
        self.grid     = make_grid()
        self.ships    = []
        self.bombs    = 2
        self.torpedos = 1
        self.shots    = 0
        self.hits     = 0

class Game:
    def __init__(self):
        self.p            = [PlayerState(), PlayerState()]
        self.current      = 0
        self.phase        = PHASE_TITLE
        self.cur_r        = 0
        self.cur_c        = 0
        self.cur_horiz    = True
        self.ship_idx     = 0
        self.bc_r         = 0
        self.bc_c         = 0
        self.mode         = MODE_NORMAL
        self.torp_horiz   = True
        self.last_was_hit = False
        self.last_cells   = []
        self.anim_timer   = 0
        self.stay_on_turn = False
        self.torp_path    = []
        self.torp_progress  = 0.0
        self.torp_total_px  = 0
        self.torp_was_hit   = False
        self.torp_affected  = []
        self.explode_timer  = 0
        self.message      = ""
        self.winner       = -1
        self.handoff_msg  = ""
        self.next_phase   = PHASE_PLACE
        self.miss_time    = {}
        self.game_mode    = MODE_MP   # MODE_SP oder MODE_MP
        self.ai_level     = AI_MEDIUM
        self.ai           = AIPlayer(AI_MEDIUM)
        self.ai_think_timer = 0
        self.ai_reaction    = ""
        self.ai_reaction_timer = 0
        self.title_cursor  = 0        # 0=MP, 1=SP-Easy, 2=SP-Med, 3=SP-Hard
        self.intro_timer = 0


g = Game()

# ══════════════════════════════════════════════════════════
#  SCHÜSSE
# ══════════════════════════════════════════════════════════

def opp(current):
    return 1 - current

def fire_normal(g, r, c):
    grid = g.p[opp(g.current)].grid
    if grid[r][c] in (MISS, HIT, SUNK):
        g.message = "Bereits beschossen!"
        return False, []
    g.p[g.current].shots += 1
    was_hit = grid[r][c] == SHIP_OK
    if was_hit:
        grid[r][c] = HIT
        g.p[g.current].hits += 1
        g.message = "TREFFER! Nochmals schiessen!"
        pyxel.play(2, 2, loop=False)
    else:
        grid[r][c] = MISS
        g.miss_time[(opp(g.current), r, c)] = pyxel.frame_count
        g.message = "Wasser..."
        pyxel.play(1, 1, loop=False)
    check_and_mark_sunk(grid, g.p[opp(g.current)].ships)
    return True, [(r, c)]

def fire_bomb(g, r, c):
    p    = g.p[g.current]
    grid = g.p[opp(g.current)].grid
    if p.bombs <= 0:
        g.message = "Keine Bomben mehr!"
        return False, []
    p.bombs -= 1
    p.shots += 1
    hits = 0
    affected = []
    for dr in range(-1, 2):
        for dc in range(-1, 2):
            nr, nc = r+dr, c+dc
            if 0 <= nr < GRID_SIZE and 0 <= nc < GRID_SIZE:
                if grid[nr][nc] == SHIP_OK:
                    grid[nr][nc] = HIT
                    p.hits += 1
                    hits += 1
                    affected.append((nr, nc))
                elif grid[nr][nc] == EMPTY:
                    grid[nr][nc] = MISS
                    affected.append((nr, nc))
    check_and_mark_sunk(grid, g.p[opp(g.current)].ships)
    g.message = ("BOMBE! " + str(hits) + " Treffer! Nochmals schiessen!"
                 if hits else "Bombe: Wasser!")
    if hits:
        pyxel.play(2, 2, loop=False)
    else:
        pyxel.play(1, 1, loop=False)
    return True, affected

def start_torpedo(g, row, col, horiz):
    p    = g.p[g.current]
    grid = g.p[opp(g.current)].grid
    if p.torpedos <= 0:
        g.message = "Kein Torpedo mehr!"
        return False
    p.torpedos -= 1
    p.shots    += 1
    path = []
    if horiz:
        for c in range(GRID_SIZE):
            path.append((row, c))
            if grid[row][c] == SHIP_OK:
                break
    else:
        for r in range(GRID_SIZE):
            path.append((r, col))
            if grid[r][col] == SHIP_OK:
                break
    g.torp_path      = path
    g.torp_progress  = 0.0
    g.torp_total_px  = len(path) * CELL
    g.torp_was_hit   = False
    g.torp_affected  = []
    g.explode_timer  = 0
    g.phase          = PHASE_TORPEDO
    return True

def apply_torpedo_damage(g):
    p    = g.p[g.current]
    grid = g.p[opp(g.current)].grid
    hit  = False
    affected = []
    for r, c in g.torp_path:
        if grid[r][c] == SHIP_OK:
            grid[r][c] = HIT
            p.hits += 1
            hit = True
            affected.append((r, c))
            break
        elif grid[r][c] == EMPTY:
            grid[r][c] = MISS
            affected.append((r, c))
    check_and_mark_sunk(grid, g.p[opp(g.current)].ships)
    g.torp_was_hit  = hit
    g.torp_affected = affected
    if hit:
        g.message = "TORPEDO! Treffer! Nochmals schiessen!"
    else:
        g.message = "Torpedo: Wasser..."

# ══════════════════════════════════════════════════════════
#  NACH RESULT
# ══════════════════════════════════════════════════════════

def go_after_result(g):
    if all_sunk(g.p[opp(g.current)].grid):
        g.winner = g.current
        g.phase  = PHASE_OVER
        pyxel.play(0, 4, loop=False)
    elif g.stay_on_turn:
        g.phase = PHASE_BATTLE
    else:
        next_p = opp(g.current)
        g.current = next_p
        if g.game_mode == MODE_SP:
            # Kein Handoff im Singleplayer
            g.phase          = PHASE_BATTLE
            g.ai_think_timer = 0
        else:
            g.phase       = PHASE_HANDOFF
            g.next_phase  = PHASE_BATTLE
            pname = "Spieler 1" if g.current == 0 else "Spieler 2"
            g.handoff_msg = pname + " ist dran!\nDruecke SPACE wenn bereit."

# ══════════════════════════════════════════════════════════
#  UPDATE
# ══════════════════════════════════════════════════════════

def update():
    global g
    
    if g.ai_reaction_timer > 0:
        g.ai_reaction_timer -= 1

    if g.phase == PHASE_TITLE:
        if key_active(pyxel.KEY_UP):
            g.title_cursor = (g.title_cursor - 1) % 4
        if key_active(pyxel.KEY_DOWN):
            g.title_cursor = (g.title_cursor + 1) % 4
        if pyxel.btnp(pyxel.KEY_SPACE) or pyxel.btnp(pyxel.KEY_RETURN):
            if g.title_cursor == 0:
                g.game_mode = MODE_MP
                g.phase       = PHASE_HANDOFF
                g.current     = 0
                g.next_phase  = PHASE_PLACE
                g.handoff_msg = "Spieler 1 ist dran!\nPlatziere deine Schiffe.\nDruecke SPACE wenn bereit."
            else:
                g.game_mode = MODE_SP
                g.ai_level  = g.title_cursor - 1
                g.ai        = AIPlayer(g.ai_level)
                g.intro_timer = 0
                g.phase     = PHASE_AI_INTRO
        return

    if g.phase == PHASE_AI_INTRO:
        g.intro_timer += 1
        if pyxel.btnp(pyxel.KEY_SPACE) or pyxel.btnp(pyxel.KEY_RETURN):
            g.phase       = PHASE_HANDOFF
            g.current     = 0
            g.next_phase  = PHASE_PLACE
            g.handoff_msg = "Platziere deine Schiffe!\nDruecke SPACE wenn bereit."
        return
    
    if g.phase == PHASE_OVER:
        if pyxel.btnp(pyxel.KEY_R):
            g = Game()
        return

    if g.phase == PHASE_HANDOFF:
        if pyxel.btnp(pyxel.KEY_SPACE) or pyxel.btnp(pyxel.KEY_RETURN):
            g.phase = g.next_phase
        return

    if g.phase == PHASE_RESULT:
        g.anim_timer += 1
        # Im SP und KI war dran: nach kurzer Pause automatisch weiter
        if g.game_mode == MODE_SP and g.current == 1:
            if g.anim_timer > 60:
                go_after_result(g)
            return
        # Spieler muss SPACE drücken
        if pyxel.btnp(pyxel.KEY_SPACE) or pyxel.btnp(pyxel.KEY_RETURN):
            go_after_result(g)
        return

    if g.phase == PHASE_TORPEDO:
        g.torp_progress += TORP_SPEED
        if g.torp_progress >= g.torp_total_px:
            g.torp_progress = float(g.torp_total_px)
            apply_torpedo_damage(g)
            g.explode_timer = 0
            g.phase = PHASE_EXPLODE
        return

    if g.phase == PHASE_EXPLODE:
        g.explode_timer += 1
        if g.explode_timer >= EXPLODE_DUR:
            g.last_was_hit  = g.torp_was_hit
            g.last_cells    = g.torp_affected
            g.stay_on_turn  = g.torp_was_hit
            g.anim_timer    = 0
            # KI-Reaktion jetzt setzen, NACH apply_torpedo_damage
            if g.game_mode == MODE_SP and g.current == 1:
                if g.torp_was_hit:
                    g.ai_reaction = random.choice(AI_REACTIONS_SUNK if
                        any(g.p[0].grid[r][c] == SUNK
                            for r, c in g.torp_affected)
                        else AI_REACTIONS_HIT)
                else:
                    g.ai_reaction = random.choice(AI_REACTIONS_MISS)
                g.ai_reaction_timer = AI_REACTION_DUR
            g.phase = PHASE_RESULT
        return

    if g.phase == PHASE_PLACE:
        length = SHIPS[g.ship_idx]
        if key_active(pyxel.KEY_UP):    g.cur_r = max(0, g.cur_r - 1)
        if key_active(pyxel.KEY_DOWN):  g.cur_r = min(GRID_SIZE-1, g.cur_r + 1)
        if key_active(pyxel.KEY_LEFT):  g.cur_c = max(0, g.cur_c - 1)
        if key_active(pyxel.KEY_RIGHT): g.cur_c = min(GRID_SIZE-1, g.cur_c + 1)
        if pyxel.btnp(pyxel.KEY_R): g.cur_horiz = not g.cur_horiz

        if pyxel.btnp(pyxel.KEY_SPACE) or pyxel.btnp(pyxel.KEY_RETURN):
            cells = ship_cells(g.cur_r, g.cur_c, length, g.cur_horiz)
            if can_place(g.p[g.current].grid, cells):
                place_ship(g.p[g.current].grid, cells)
                g.p[g.current].ships.append(cells)
                pyxel.play(0, 0, loop=False)
                g.ship_idx += 1
                g.cur_r, g.cur_c, g.cur_horiz = 0, 0, True
                g.message = ""
                if g.ship_idx >= len(SHIPS):
                    if g.game_mode == MODE_SP:
                        ai_grid  = g.p[1].grid
                        ai_ships = []
                        for cells in g.ai.auto_place(ai_grid, list(SHIPS)):
                            ai_ships.append(cells)
                        g.p[1].ships = ai_ships
                        g.current    = 0
                        g.ship_idx   = -1
                        g.phase      = PHASE_HANDOFF
                        g.next_phase = PHASE_BATTLE
                        g.handoff_msg = "Schiffe platziert!\nViel Erfolg!\nDruecke SPACE wenn bereit."
                    elif g.current == 0:
                        g.current     = 1
                        g.ship_idx    = 0
                        g.phase       = PHASE_HANDOFF
                        g.next_phase  = PHASE_PLACE
                        g.handoff_msg = "Spieler 2 ist dran!\nPlatziere deine Schiffe.\nDruecke SPACE wenn bereit."
                    else:
                        g.current     = 0
                        g.ship_idx    = -1
                        g.phase       = PHASE_HANDOFF
                        g.next_phase  = PHASE_BATTLE
                        g.handoff_msg = "Alle Schiffe platziert!\nSpieler 1 beginnt.\nDruecke SPACE wenn bereit."
            else:
                g.message = "Zu nah an einem anderen Schiff!"
                pyxel.play(0, 5, loop=False)
        return

    if g.phase == PHASE_BATTLE and g.game_mode == MODE_SP and g.current == 1:
        g.ai_think_timer += 1
        if g.ai_think_timer >= AI_THINK_DELAY:
            g.ai_think_timer = 0
            p_ai   = g.p[1]
            target_grid = g.p[0].grid

            weapon, torp_h = g.ai.choose_weapon(p_ai, target_grid)
            r, c = g.ai.choose_shot(target_grid, g.p[0].ships, g.p[0].ships)

            if weapon == MODE_TORPEDO and p_ai.torpedos > 0:
                g.torp_horiz = torp_h if torp_h is not None else True
                start_torpedo(g, r, c, g.torp_horiz)
            elif weapon == MODE_BOMB and p_ai.bombs > 0:
                fired, affected = fire_bomb(g, r, c)
                if fired:
                    opp_grid = g.p[0].grid
                    was_hit  = any(opp_grid[rr][cc] in (HIT, SUNK) for rr, cc in affected)
                    was_sunk = any(opp_grid[rr][cc] == SUNK for rr, cc in affected)
                    g.ai.notify_result(r, c, was_hit, was_sunk, opp_grid)
                    g.last_was_hit = was_hit
                    g.last_cells   = affected
                    g.stay_on_turn = was_hit
                    g.anim_timer   = 0
                    g.phase        = PHASE_RESULT
                    if was_sunk:
                        g.ai_reaction = random.choice(AI_REACTIONS_SUNK)
                    elif was_hit:
                        g.ai_reaction = random.choice(AI_REACTIONS_HIT)
                    else:
                        g.ai_reaction = random.choice(AI_REACTIONS_MISS)
                    g.ai_reaction_timer = AI_REACTION_DUR
            else:
                fired, affected = fire_normal(g, r, c)
                if fired:
                    opp_grid = g.p[0].grid
                    was_hit  = any(opp_grid[rr][cc] in (HIT, SUNK) for rr, cc in affected)
                    was_sunk = any(opp_grid[rr][cc] == SUNK for rr, cc in affected)
                    g.ai.notify_result(r, c, was_hit, was_sunk, opp_grid)
                    g.last_was_hit = was_hit
                    g.last_cells   = affected
                    g.stay_on_turn = was_hit
                    g.anim_timer   = 0
                    g.phase        = PHASE_RESULT
                    if was_sunk:
                        g.ai_reaction = random.choice(AI_REACTIONS_SUNK)
                    elif was_hit:
                        g.ai_reaction = random.choice(AI_REACTIONS_HIT)
                    else:
                        g.ai_reaction = random.choice(AI_REACTIONS_MISS)
                    g.ai_reaction_timer = AI_REACTION_DUR
        return

    if g.phase == PHASE_BATTLE:
        if key_active(pyxel.KEY_UP):    g.bc_r = max(0, g.bc_r - 1)
        if key_active(pyxel.KEY_DOWN):  g.bc_r = min(GRID_SIZE-1, g.bc_r + 1)
        if key_active(pyxel.KEY_LEFT):  g.bc_c = max(0, g.bc_c - 1)
        if key_active(pyxel.KEY_RIGHT): g.bc_c = min(GRID_SIZE-1, g.bc_c + 1)

        if pyxel.btnp(pyxel.KEY_1): g.mode = MODE_NORMAL
        if pyxel.btnp(pyxel.KEY_2): g.mode = MODE_BOMB
        if pyxel.btnp(pyxel.KEY_3): g.mode = MODE_TORPEDO
        if pyxel.btnp(pyxel.KEY_R) and g.mode == MODE_TORPEDO:
            g.torp_horiz = not g.torp_horiz

        if pyxel.btnp(pyxel.KEY_SPACE) or pyxel.btnp(pyxel.KEY_RETURN):
            if g.mode == MODE_NORMAL:
                fired, affected = fire_normal(g, g.bc_r, g.bc_c)
                if fired:
                    opp_grid = g.p[opp(g.current)].grid
                    was_hit = any(opp_grid[r][c] in (HIT, SUNK) for r, c in affected)
                    g.last_was_hit = was_hit
                    g.last_cells   = affected
                    g.stay_on_turn = was_hit
                    g.anim_timer   = 0
                    g.phase        = PHASE_RESULT
            elif g.mode == MODE_BOMB:
                fired, affected = fire_bomb(g, g.bc_r, g.bc_c)
                if fired:
                    opp_grid = g.p[opp(g.current)].grid
                    was_hit = any(opp_grid[r][c] in (HIT, SUNK) for r, c in affected)
                    g.last_was_hit = was_hit
                    g.last_cells   = affected
                    g.stay_on_turn = was_hit
                    g.anim_timer   = 0
                    g.phase        = PHASE_RESULT
            elif g.mode == MODE_TORPEDO:
                start_torpedo(g, g.bc_r, g.bc_c, g.torp_horiz)

# ══════════════════════════════════════════════════════════
#  DRAW – HILFSFUNKTIONEN
# ══════════════════════════════════════════════════════════

def shadow_text(x, y, s, col):
    pyxel.text(x+1, y+1, s, BLACK)
    pyxel.text(x, y, s, col)


SHIP_SPRITES_H = {
    5: (  64,  128),
    4: (  64,  144),
    3: (  64,  192),
    2: (  64, 176),
}
SHIP_SPRITES_V = {
    5: ( 128,  48),
    4: (128,  144),
    3: (112,  192),
    2: (80,  216),
}

def draw_ship_sprite(ox, oy, cells, horiz):
    if not cells:
        return
    length = len(cells)
    r0, c0 = cells[0]
    x = ox + c0 * CELL
    y = oy + r0 * CELL

    if horiz and length in SHIP_SPRITES_H:
        u, v = SHIP_SPRITES_H[length]
        pyxel.blt(x, y, 0, u, v, length * CELL, CELL,)
    elif not horiz and length in SHIP_SPRITES_V:
        u, v = SHIP_SPRITES_V[length]
        pyxel.blt(x, y, 0, u, v, CELL, length * CELL,)
    else:
        # Fallback: graues Rechteck
        w = length * CELL if horiz else CELL
        h = CELL if horiz else length * CELL
        pyxel.rect(x+1, y+1, w-2, h-2, DARK_GRAY)

def draw_grid(ox, oy, grid, show_ships, ships=None,
              cursor_r=-1, cursor_c=-1,
              highlight_cells=None, anim_timer=0):
                  
    WATER_U = [0, 16]   # <-- X-Koordinaten deiner 2 Frames
    WATER_V = [224, 224]    # <-- Y-Koordinaten deiner 2 Frames
    WATER_IMG = 1

    pyxel.rect(ox-1, oy-1, GRID_SIZE*CELL+2, GRID_SIZE*CELL+2, INDIGO)

    for r in range(GRID_SIZE):
        for c in range(GRID_SIZE):
            x = ox + c * CELL
            y = oy + r * CELL
            state = grid[r][c]

            if state == MISS:
                pyxel.blt(x, y, WATER_IMG, WATER_U[frame], WATER_V[frame], 16, 16, 0)

                # Leichte Abdunklung
                for py_ in range(y, y + CELL):
                    for px_ in range(x, x + CELL):
                        if (px_ - py_) % 4 == 0:
                            pyxel.pset(px_, py_, DARK_BLUE)
                # Splash
                player_id = opp(g.current) if not show_ships else g.current
                if (player_id, r, c) in g.miss_time:
                    t = pyxel.frame_count - g.miss_time[(player_id, r, c)]
                    if t < 180:
                        pyxel.blt(x, y, 1, 64, 32, 16, 16, 0)
                # Miss-Markierung
                mx, my = x + CELL // 2, y + CELL // 2
                pyxel.pset(mx-2, my-2, LIGHT_GRAY)
                pyxel.pset(mx+2, my-2, LIGHT_GRAY)
                pyxel.pset(mx,   my,   LIGHT_GRAY)
                pyxel.pset(mx-2, my+2, LIGHT_GRAY)
                pyxel.pset(mx+2, my+2, LIGHT_GRAY)
                
            elif state == HIT:
                pyxel.rect(x+1, y+1, CELL-2, CELL-2, BROWN)
            elif state == SUNK:
                pyxel.blt(x, y, 1, 80, 32, 16, 16, 0)
                # kleine Glutpunkte
                if (pyxel.frame_count // 10) % 2 == 0:
                    pyxel.pset(x+6, y+7, ORANGE)
                    pyxel.pset(x+9, y+8, ORANGE)
            elif state == SHIP_OK and show_ships:
                pyxel.rect(x+1, y+1, CELL-2, CELL-2, DARK_GRAY)
            else:
                # ── Dein eigenes Wasser-Sprite (selbe Konstanten wie oben)
                WATER_U = [0, 16]   # <-- X-Koordinaten
                WATER_V = [224, 224]    # <-- Y-Koordinaten
                WATER_IMG = 0       # <-- Image-Bank
                frame = (pyxel.frame_count // 25 + r + c) % 2
                pyxel.blt(x, y, WATER_IMG, WATER_U[frame], WATER_V[frame], 16, 16, 0)
                
            pyxel.rectb(x, y, CELL, CELL, COL_GRID)
            
            if state in (HIT, SUNK):
                ef = pyxel.frame_count % 3 if state == HIT else 2
                pyxel.blt(x, y, 1, ef * 16 + 16, 16, 16, 16, 0)

    # Schiffe zeichnen
    if show_ships and ships:
        for ship in ships:
            if not ship:
                continue
            sh = len(ship) < 2 or ship[0][0] == ship[1][0]
            draw_ship_sprite(ox, oy, ship, sh)

    # Highlight-Overlay
    if highlight_cells:
        blink_on = (anim_timer // 8) % 2 == 0
        for r, c in highlight_cells:
            x = ox + c * CELL
            y = oy + r * CELL
            state = grid[r][c]
            if state in (HIT, SUNK):
                flash_col = WHITE if blink_on else YELLOW
                pyxel.rectb(x+1, y+1, CELL-2, CELL-2, flash_col)
                exp_frame = min(anim_timer // 8, 4)
                pyxel.blt(x, y, 1, exp_frame * 16 + 16, 16, 16, 16, 0)
            elif state == MISS:
                if blink_on:
                    pyxel.rect(x+1, y+1, CELL-2, CELL-2, CYAN)

    if 0 <= cursor_r < GRID_SIZE and 0 <= cursor_c < GRID_SIZE:
        cx = ox + cursor_c * CELL
        cy = oy + cursor_r * CELL
        pyxel.rectb(cx,   cy,   CELL, CELL, YELLOW)
        pyxel.rectb(cx+1, cy+1, CELL-2, CELL-2, ORANGE)

def draw_ship_preview(ox, oy, grid, row, col, length, horiz):
    cells = ship_cells(row, col, length, horiz)
    ok    = can_place(grid, cells)
    col_p = GREEN if ok else RED
    for r, c in cells:
        if 0 <= r < GRID_SIZE and 0 <= c < GRID_SIZE:
            x = ox + c * CELL
            y = oy + r * CELL
            pyxel.rect(x+1, y+1, CELL-2, CELL-2, col_p)
            pyxel.rectb(x, y, CELL, CELL, COL_GRID)

def draw_damage_bars():
    bar_w   = GRID_SIZE * CELL
    bar_h   = 6
    bar_y   = SCREEN_H - 16
    label_y = bar_y - 9

    for i in range(2):
        ox  = LEFT_X if i == 0 else RIGHT_X
        pct = ship_hit_percent(g.p[i])
        fill = int(bar_w * pct)

        if g.game_mode == MODE_SP:
            name = "Du" if i == 0 else AI_FULLNAMES[g.ai_level]
        else:
            name = "S1" if i == 0 else "S2"
        pct_s = str(int(pct * 100)) + "%"

        pyxel.rect(ox, bar_y, bar_w, bar_h, DARK_GRAY)
        col = GREEN if pct < 0.5 else (YELLOW if pct < 0.8 else RED)
        if fill > 0:
            pyxel.rect(ox, bar_y, fill, bar_h, col)
        pyxel.rectb(ox, bar_y, bar_w, bar_h, LIGHT_GRAY)
        pyxel.text(ox, label_y, name + " Schaden: " + pct_s, COL_DIM)

def draw_battle_ui(show_own, highlight_cells=None, anim_timer=0):
    o = opp(g.current)
    p = g.p[g.current]

    if g.game_mode == MODE_SP:
        pname     = "Du"
        ai_name   = AI_FULLNAMES[g.ai_level]
        own_label = "Dein Feld (A)" if not show_own else "Dein Feld"
        # Im SP: linkes Grid = Spieler 0, rechtes = KI (1) — immer
        left_grid   = g.p[0].grid
        left_ships  = g.p[0].ships
        right_grid  = g.p[1].grid
        show_left   = show_own if g.current == 0 else False
        header_left = own_label
    else:
        pname       = "Spieler 1" if g.current == 0 else "Spieler 2"
        left_grid   = g.p[g.current].grid
        left_ships  = g.p[g.current].ships
        right_grid  = g.p[o].grid
        show_left   = show_own
        header_left = "Dein Feld (A)" if not show_own else "Dein Feld"

    pyxel.text(LEFT_X,  4,  header_left, COL_DIM if not show_own else CYAN)
    pyxel.text(RIGHT_X, 4,  "Gegner",    COL_TEXT)

    if g.game_mode == MODE_SP and g.current == 1:
        # KI ist dran – zeige KI-Stats nicht, zeige Spieler-Stats
        p_display = g.p[0]
    else:
        p_display = p
    pyxel.text(LEFT_X, 14,
        ("Du" if g.game_mode == MODE_SP else pname) +
        " B:" + str(g.p[0].bombs if g.game_mode == MODE_SP else p_display.bombs) +
        " T:" + str(g.p[0].torpedos if g.game_mode == MODE_SP else p_display.torpedos) +
        " Schuss:" + str(g.p[0].shots if g.game_mode == MODE_SP else p_display.shots),
        COL_TEXT)

    if g.game_mode == MODE_SP:
        # Im SP: Highlight auf dem richtigen Grid
        if g.current == 1:
            # KI hat auf Spielerfeld geschossen -> Highlight links
            draw_grid(LEFT_X,  GRID_Y, left_grid,
                      show_ships=show_left, ships=left_ships,
                      highlight_cells=highlight_cells, anim_timer=anim_timer)
            draw_grid(RIGHT_X, GRID_Y, right_grid, show_ships=False)
        else:
            # Spieler hat auf KI-Feld geschossen -> Highlight rechts
            draw_grid(LEFT_X,  GRID_Y, left_grid,
                      show_ships=show_left, ships=left_ships)
            draw_grid(RIGHT_X, GRID_Y, right_grid, show_ships=False,
                      highlight_cells=highlight_cells, anim_timer=anim_timer)
    else:
        draw_grid(LEFT_X,  GRID_Y, left_grid,
                  show_ships=show_left, ships=left_ships)
        draw_grid(RIGHT_X, GRID_Y, right_grid, show_ships=False,
                  highlight_cells=highlight_cells, anim_timer=anim_timer)

    # Waffenauswahl nur wenn Spieler 0 dran ist
    if not (g.game_mode == MODE_SP and g.current == 1):
        torp_dir = "H" if g.torp_horiz else "V"
        p0 = g.p[0] if g.game_mode == MODE_SP else p
        labels = ["1:Normal", "2:Bombe(" + str(p0.bombs) + ")",
                  "3:Torpedo(" + str(p0.torpedos) + ")[" + torp_dir + "]"]
        bx = LEFT_X
        for i, label in enumerate(labels):
            col = YELLOW if g.mode == i else COL_DIM
            pyxel.text(bx, GRID_Y + GRID_SIZE*CELL + 4, label, col)
            bx += len(label)*4 + 4

def draw_ai_reaction():
    if g.ai_reaction and g.ai_reaction_timer > 0:
        col = RED if g.ai_reaction in AI_REACTIONS_HIT + AI_REACTIONS_SUNK else CYAN
        rx  = SCREEN_W // 2 - len(g.ai_reaction) * 2
        ry  = GRID_Y + GRID_SIZE * CELL // 2 - 4
        if g.ai_reaction_timer > 20 or (g.ai_reaction_timer // 5) % 2 == 0:
            pyxel.rect(rx-4, ry-3, len(g.ai_reaction)*4+8, 11, BLACK)
            pyxel.rectb(rx-5, ry-4, len(g.ai_reaction)*4+10, 13, col)
            shadow_text(rx, ry, g.ai_reaction, col)
    
# ══════════════════════════════════════════════════════════
#  DRAW – TORPEDO ANIMATION
# ══════════════════════════════════════════════════════════

def draw_torpedo_phase():
    show_own = pyxel.btn(KEY_REVEAL)

    if g.game_mode == MODE_SP:
        # Linkes Grid = immer Spieler, rechtes = immer KI
        player_grid = g.p[0].grid
        player_ships = g.p[0].ships
        enemy_grid  = g.p[1].grid
        horiz = g.torp_horiz
        path  = g.torp_path
        prog  = g.torp_progress
        # Torpedo zeigen auf dem Feld des Ziels (rechts wenn KI schiesst auf Spieler: links)
        if g.current == 1:
            # KI schiesst auf Spieler -> Torpedo auf linkem Grid
            torp_ox = LEFT_X
            torp_grid_show = True  # Spieler sieht sein eigenes Feld
        else:
            torp_ox = RIGHT_X
            torp_grid_show = False
    else:
        o = opp(g.current)
        player_grid  = g.p[g.current].grid
        player_ships = g.p[g.current].ships
        enemy_grid   = g.p[o].grid
        horiz        = g.torp_horiz
        path         = g.torp_path
        prog         = g.torp_progress
        torp_ox      = RIGHT_X
        torp_grid_show = False

    pyxel.text(LEFT_X,  4, "Dein Feld (A)" if not show_own else "Dein Feld", COL_DIM)
    pyxel.text(RIGHT_X, 4, "Gegner", COL_TEXT)

    if g.game_mode == MODE_SP:
        draw_grid(LEFT_X,  GRID_Y, player_grid,
                  show_ships=show_own, ships=player_ships)
        draw_grid(RIGHT_X, GRID_Y, enemy_grid, show_ships=False)
    else:
        draw_grid(LEFT_X,  GRID_Y, player_grid,
                  show_ships=show_own, ships=player_ships)
        draw_grid(RIGHT_X, GRID_Y, enemy_grid, show_ships=False)

    if not path:
        return

    cells_behind   = int(prog // CELL)
    offset_in_cell = prog - cells_behind * CELL

    for i in range(min(cells_behind, len(path))):
        r, c = path[i]
        x = torp_ox + c * CELL
        y = GRID_Y  + r * CELL
        pyxel.blt(x, y, 1, 48, 32, 16, 16, 0)

    if cells_behind > 0:
        r0, c0     = path[0]
        ri         = min(cells_behind, len(path)-1)
        ri_r, ri_c = path[ri]
        pyxel.line(torp_ox + c0*CELL + CELL//2,    GRID_Y + r0*CELL   + CELL//2,
                   torp_ox + ri_c*CELL + CELL//2,  GRID_Y + ri_r*CELL + CELL//2,
                   ORANGE)

    head_cell = min(cells_behind, len(path)-1)
    hr, hc    = path[head_cell]
    if horiz:
        hx = torp_ox + hc * CELL + int(offset_in_cell)
        hy = GRID_Y  + hr * CELL
        pyxel.blt(hx, hy, 1, 0, 32, 16, 16, 0)
    else:
        hx = torp_ox + hc * CELL
        hy = GRID_Y  + hr * CELL + int(offset_in_cell)
        pyxel.blt(hx, hy, 1, 16, 32, 16, 16, 0)

    shadow_text(LEFT_X, GRID_Y + GRID_SIZE*CELL + 8, "Torpedo unterwegs...", ORANGE)

# ══════════════════════════════════════════════════════════
#  DRAW – EXPLOSIONS-PHASE
# ══════════════════════════════════════════════════════════

def draw_explode_phase():
    show_own = pyxel.btn(KEY_REVEAL)
    t   = g.explode_timer
    hit = g.torp_was_hit

    if g.game_mode == MODE_SP:
        player_grid  = g.p[0].grid
        player_ships = g.p[0].ships
        enemy_grid   = g.p[1].grid
        # Explosion auf dem Feld des Ziels
        explode_ox = LEFT_X if g.current == 1 else RIGHT_X
    else:
        o            = opp(g.current)
        player_grid  = g.p[g.current].grid
        player_ships = g.p[g.current].ships
        enemy_grid   = g.p[o].grid
        explode_ox   = RIGHT_X

    pyxel.text(LEFT_X,  4, "Dein Feld", COL_DIM)
    pyxel.text(RIGHT_X, 4, "Gegner",    COL_TEXT)
    draw_grid(LEFT_X,  GRID_Y, player_grid,
              show_ships=show_own, ships=player_ships)
    draw_grid(RIGHT_X, GRID_Y, enemy_grid, show_ships=False)

    if not g.torp_path:
        return

    er, ec = g.torp_path[-1]
    ex = explode_ox + ec * CELL
    ey = GRID_Y     + er * CELL

    if hit:
        if   t < 10: ef = 1
        elif t < 20: ef = 2
        elif t < 32: ef = 3
        else:        ef = 4
        for dx, dy in [(0,0),(-2,-2),(2,-2),(-2,2),(2,2)]:
            pyxel.blt(ex+dx, ey+dy, 1, ef*16+16, 16, 16, 16, 0)
        pyxel.rectb(ex, ey, CELL, CELL, YELLOW if (t//4)%2==0 else RED)
        if t < 8:
            for _ in range((8-t)*3):
                pyxel.pset(random.randint(explode_ox, explode_ox+GRID_SIZE*CELL),
                           random.randint(GRID_Y, GRID_Y+GRID_SIZE*CELL), YELLOW)
        shadow_text(LEFT_X, GRID_Y+GRID_SIZE*CELL+8, "TREFFER!", RED)
    else:
        if t < 20:
            pyxel.blt(ex, ey, 1, 64, 32, 16, 16, 0)
            if (t//6)%2==0:
                pyxel.rectb(ex, ey, CELL, CELL, CYAN)
        shadow_text(LEFT_X, GRID_Y+GRID_SIZE*CELL+8, "Wasser...", CYAN)

# ══════════════════════════════════════════════════════════
#  DRAW HAUPTFUNKTION
# ══════════════════════════════════════════════════════════

def draw():
    if g.phase == PHASE_TITLE:
        pyxel.cls(COL_BG)
        for row in range(SCREEN_H // CELL + 1):
            for col in range(SCREEN_W // CELL + 1):
                frame = (row + col) % 3
                pyxel.blt(col * CELL, row * CELL, 1, frame * 16, 0, 16, 16, 0)
        for y in range(0, SCREEN_H, 3):
            for x in range(0, SCREEN_W, 3):
                pyxel.pset(x, y, DARK_BLUE)

        title  = "SCHIFFE VERSENKEN"
        cx     = SCREEN_W // 2
        cy     = SCREEN_H // 2

        bw, bh = 210, 110
        bx = cx - bw // 2
        by = cy - bh // 2 - 16
        pyxel.rect (bx-2, by-2, bw+4, bh+4, DARK_BLUE)
        pyxel.rectb(bx-2, by-2, bw+4, bh+4, INDIGO)
        pyxel.rectb(bx-1, by-1, bw+2, bh+2, DARK_GRAY)

        tx = cx - len(title) * 2
        ty = by + 8
        shadow_text(tx, ty, title, WHITE)
        pyxel.line(cx-55, ty+10, cx+55, ty+10, INDIGO)

        options = [
            "2 Spieler (Lokal)",
            "1 Spieler - Leicht",
            "1 Spieler - Mittel",
            "1 Spieler - Schwer",
        ]
        for i, opt in enumerate(options):
            oy_  = ty + 18 + i * 14
            ox_  = cx - len(opt) * 2
            if g.title_cursor == i:
                pyxel.rect(ox_-3, oy_-2, len(opt)*4+6, 11, INDIGO)
                shadow_text(ox_, oy_, opt, YELLOW)
            else:
                pyxel.text(ox_, oy_, opt, COL_DIM)

        if (pyxel.frame_count // 25) % 2 == 0:
            prompt = "SPACE = Bestaetigen"
            shadow_text(cx - len(prompt)*2, ty + 80, prompt, CYAN)
        return

    if g.phase == PHASE_AI_INTRO:
        pyxel.cls(BLACK)
        for i in range(0, SCREEN_W, 16):
            for j in range(0, SCREEN_H, 16):
                pyxel.rectb(i, j, 16, 16, DARK_BLUE)

        cx = SCREEN_W // 2
        cy = SCREEN_H // 2
        bw, bh = 240, 160
        bx = cx - bw // 2
        by = cy - bh // 2

        # Rahmenfarbe je Schwierigkeit
        frame_col = [GREEN, YELLOW, RED][g.ai_level]

        pyxel.rect (bx-2, by-2, bw+4, bh+4, DARK_BLUE)
        pyxel.rectb(bx-2, by-2, bw+4, bh+4, frame_col)
        pyxel.rectb(bx-1, by-1, bw+2, bh+2, DARK_GRAY)

        title = "DEIN GEGNER"
        shadow_text(cx - len(title)*2, by + 10, title, WHITE)
        pyxel.line(bx+10, by+20, bx+bw-10, by+20, INDIGO)

        # Portrait zeichnen, leicht schwebend animiert
        bob = int(2 * (1 if (pyxel.frame_count // 20) % 2 == 0 else -1))
        portrait_u = g.ai_level * 32
        px_ = cx - 16
        py_ = by + 28 + bob
        pyxel.blt(px_, py_, 1, portrait_u, 48, 32, 32, 0)

        name = AI_FULLNAMES[g.ai_level]
        shadow_text(cx - len(name)*2, by + 66, name, frame_col)

        diff = "Schwierigkeit: " + AI_DIFFICULTY_LABEL[g.ai_level]
        pyxel.text(cx - len(diff)*2, by + 78, diff, COL_DIM)

        quote = AI_QUOTES[g.ai_level]
        pyxel.text(cx - len(quote)*2, by + 94, quote, CYAN)

        pyxel.line(bx+10, by+108, bx+bw-10, by+108, INDIGO)

        # Kleine Beschreibung je Stufe
        descs = [
            "Schiesst meist zufaellig drauflos.",
            "Sucht systematisch nach Treffern.",
            "Erfahren, gefaehrlich, kaum zu schlagen.",
        ]
        desc = descs[g.ai_level]
        pyxel.text(cx - len(desc)*2, by + 116, desc, WHITE)

        if (pyxel.frame_count // 25) % 2 == 0:
            prompt = "SPACE = Bereit machen"
            shadow_text(cx - len(prompt)*2, by + 138, prompt, YELLOW)
        return

    if g.phase == PHASE_HANDOFF:
        pyxel.cls(BLACK)
        for i in range(0, SCREEN_W, 16):
            for j in range(0, SCREEN_H, 16):
                pyxel.rectb(i, j, 16, 16, DARK_BLUE)
        lines   = g.handoff_msg.split("\n")
        bw = max(len(l) for l in lines)*4 + 20
        bh = len(lines)*12 + 16
        bx = SCREEN_W//2 - bw//2
        by = SCREEN_H//2 - bh//2
        pyxel.rect(bx, by, bw, bh, DARK_BLUE)
        pyxel.rectb(bx, by, bw, bh, CYAN)
        pyxel.rectb(bx+1, by+1, bw-2, bh-2, INDIGO)
        start_y = by + 8
        for i, ln in enumerate(lines):
            shadow_text(SCREEN_W//2 - len(ln)*2, start_y + i*12, ln, WHITE)
        return

    pyxel.cls(COL_BG)
    for i in range(0, SCREEN_W, 8):
        wave_y = (pyxel.frame_count // 30 + i // 8) % 2
        pyxel.pset(i, SCREEN_H - 4 + wave_y, INDIGO)

    if g.phase == PHASE_PLACE:
        pname  = "Spieler 1" if g.current == 0 else "Spieler 2"
        length = SHIPS[g.ship_idx]
        orient = "Horizontal" if g.cur_horiz else "Vertikal"
        shadow_text(LEFT_X, 4,
            pname + ": Schiff " + str(g.ship_idx+1) + "/" +
            str(len(SHIPS)) + "  Laenge: " + str(length), CYAN)
        pyxel.text(LEFT_X, 14, "Pfeile=Bew  R=Drehen  SPACE=Platz", COL_DIM)
        draw_grid(LEFT_X, GRID_Y, g.p[g.current].grid,
                  show_ships=True, ships=g.p[g.current].ships,
                  cursor_r=g.cur_r, cursor_c=g.cur_c)
        draw_ship_preview(LEFT_X, GRID_Y, g.p[g.current].grid,
                          g.cur_r, g.cur_c, length, g.cur_horiz)
        pyxel.text(LEFT_X, GRID_Y + GRID_SIZE*CELL + 4, "Ausrichtung: " + orient, WHITE)
        if g.message:
            shadow_text(LEFT_X, GRID_Y + GRID_SIZE*CELL + 14, g.message, RED)
        return

    if g.phase == PHASE_TORPEDO:
        draw_torpedo_phase()
        return

    if g.phase == PHASE_EXPLODE:
        draw_explode_phase()
        draw_damage_bars()
        return

    show_own = pyxel.btn(KEY_REVEAL)

    if g.phase == PHASE_BATTLE:
        draw_battle_ui(show_own=show_own)
        if g.mode == MODE_TORPEDO:
            tdir = "H (R=wechseln)" if g.torp_horiz else "V (R=wechseln)"
            pyxel.text(LEFT_X, GRID_Y+GRID_SIZE*CELL+14, "Torpedo: "+tdir, CYAN)
        else:
            pyxel.text(LEFT_X, GRID_Y+GRID_SIZE*CELL+14, g.message, WHITE)
        pyxel.text(LEFT_X, GRID_Y+GRID_SIZE*CELL+24,
             "SPACE=Schiessen  A=Mein Feld", COL_DIM)
        pyxel.rectb(RIGHT_X+g.bc_c*CELL,   GRID_Y+g.bc_r*CELL,   CELL, CELL, YELLOW)
        pyxel.rectb(RIGHT_X+g.bc_c*CELL+1, GRID_Y+g.bc_r*CELL+1, CELL-2, CELL-2, ORANGE)
        draw_damage_bars()
        # KI denkt nach...
        if g.game_mode == MODE_SP and g.current == 1:
            dots = "." * ((g.ai_think_timer // 12) % 4)
            shadow_text(RIGHT_X, GRID_Y + GRID_SIZE*CELL + 14,
                        AI_FULLNAMES[g.ai_level] + " denkt" + dots, ORANGE)
        return

    if g.phase == PHASE_RESULT:
        draw_battle_ui(show_own=show_own,
                       highlight_cells=g.last_cells,
                       anim_timer=g.anim_timer)
        if g.game_mode == MODE_SP and g.current == 1:
            draw_ai_reaction()
        msg_col = RED if g.last_was_hit else COL_DIM
        shadow_text(LEFT_X, GRID_Y+GRID_SIZE*CELL+14, g.message, msg_col)
        if (g.anim_timer // 15) % 2 == 0:
            if g.stay_on_turn:
                shadow_text(LEFT_X, GRID_Y+GRID_SIZE*CELL+24,
                            "SPACE = nochmals schiessen!", YELLOW)
            else:
                pyxel.text(LEFT_X, GRID_Y+GRID_SIZE*CELL+24,
                     "SPACE = weiter  A=Mein Feld", WHITE)
        draw_damage_bars()
        return
 
    if g.phase == PHASE_OVER:
        pyxel.cls(BLACK)
        for i in range(0, SCREEN_W, 16):
            for j in range(0, SCREEN_H, 16):
                pyxel.rectb(i, j, 16, 16, DARK_BLUE)
    
        if g.game_mode == MODE_SP:
            wname = "Du" if g.winner == 0 else AI_FULLNAMES[g.ai_level]
        else:
            wname = "Spieler 1" if g.winner == 0 else "Spieler 2"
        pw = g.p[g.winner]
        pl = g.p[opp(g.winner)]
        
        bw = 200
        bh = 110
        bx = SCREEN_W // 2 - bw // 2
        by = SCREEN_H // 2 - bh // 2
    
        pyxel.rect (bx,     by,     bw,     bh,     DARK_BLUE)
        pyxel.rectb(bx,     by,     bw,     bh,     YELLOW)
        pyxel.rectb(bx + 1, by + 1, bw - 2, bh - 2, ORANGE)
        
        title = wname + " gewinnt!"
        tx = SCREEN_W // 2 - len(title) * 2
        shadow_text(tx, by + 12, title, YELLOW)
        
        pyxel.line(bx + 10, by + 26, bx + bw - 10, by + 26, INDIGO)
        
        line1 = "Gewinner:  Schuss:" + str(pw.shots) + "  Treffer:" + str(pw.hits)
        line2 = "Verlierer: Schuss:" + str(pl.shots) + "  Treffer:" + str(pl.hits)
        lx1 = SCREEN_W // 2 - len(line1) * 2
        lx2 = SCREEN_W // 2 - len(line2) * 2
        pyxel.text(lx1, by + 34, line1, WHITE)
        pyxel.text(lx2, by + 46, line2, COL_DIM)
    
        acc_w = round(pw.hits / pw.shots * 100) if pw.shots > 0 else 0
        acc_l = round(pl.hits / pl.shots * 100) if pl.shots > 0 else 0
        acc1 = "Genauigkeit: " + str(acc_w) + "%"
        acc2 = "Genauigkeit: " + str(acc_l) + "%"
        pyxel.text(SCREEN_W // 2 - len(acc1) * 2, by + 58, acc1, CYAN)
        pyxel.text(SCREEN_W // 2 - len(acc2) * 2, by + 70, acc2, DARK_GRAY)
        
        pyxel.line(bx + 10, by + 82, bx + bw - 10, by + 82, INDIGO)
        
        if (pyxel.frame_count // 20) % 2 == 0:
            prompt = "R druecken = Neues Spiel"
            px_ = SCREEN_W // 2 - len(prompt) * 2
            shadow_text(px_, by + 90, prompt, CYAN)

# ══════════════════════════════════════════════════════════
#  START
# ══════════════════════════════════════════════════════════
pyxel.init(SCREEN_W, SCREEN_H, title="Schiffe Versenken 2 Spieler")
pyxel.load("res.pyxres")
pyxel.load("1res.pyxres")
load_sprites()
setup_sounds()
pyxel.playm(0, loop=True)
pyxel.run(update, draw)