"""Is pond.py a fair game? Play it many times with no window, and count the endings.

    python fair_game.py            40 games for each of two players
    python fair_game.py 100        100 games each

Two simple players take the place of your mouse. "careful" swims away from every
dangerous head that comes close, hides under a leaf when one is in reach, and eats
only food that is far from danger. "greedy" swims at the nearest food and never
looks at the hunters. The game is fair when careful wins most games and greedy
does not. The rules game is played: pick() chooses each hunter's move, no model.

The numbers at the top of pond.py (NEAR, QUICK, the speeds) were chosen with this
test. Change one of them, run this again, and see what it does to the game.
"""
import math
import pathlib
import random
import statistics
import sys

ASSETS = pathlib.Path(__file__).resolve().parent.parent      # pond.py: one folder up from checks/
FRAMES_A_SECOND = 35              # the real window drew 34 to 36 on our laptop (pond_plays.py)
LONGEST = 12 * 60                 # a game that lasts 12 minutes is stopped


def careful(pond, game):
    """Where the careful player puts the mouse."""
    head = game.player.points[0]
    dangers = [hunter.points[0] for hunter in game.hunters if hunter.size > game.player.size]
    close = [place for place in dangers if math.dist(head, place) < 170]
    if close:
        if game.under_leaf():
            return head                           # safe under a leaf: stay there
        for leaf in pond.LEAVES:
            # A leaf that the fish reaches before any close hunter does.
            if math.dist(head, leaf) < min(200, min(math.dist(place, leaf) for place in close)):
                return leaf
        # Try sixteen directions. Take the one that ends farthest from every dangerous head.
        best, best_score = head, -1.0
        for k in range(16):
            there = pond.walk(head, k * math.pi / 8, 90)
            there = [min(max(there[0], 15), pond.WIDE - 15), min(max(there[1], 15), pond.HIGH - 15)]
            score = min(math.dist(there, place) for place in dangers)
            score = score - 0.5 * (90 - math.dist(head, there))       # a wall cuts the step short
            if score > best_score:
                best, best_score = there, score
        return best
    prey = [hunter for hunter in game.hunters if hunter.size < game.player.size]
    if prey:
        nearest = min(prey, key=lambda hunter: math.dist(head, hunter.points[0]))
        return nearest.points[len(nearest.points) // 2]
    safe = [dot for dot in game.food if all(math.dist(dot, place) > 210 for place in dangers)]
    if safe:
        return min(safe, key=lambda dot: math.dist(head, dot))
    return min(pond.LEAVES, key=lambda leaf: math.dist(head, leaf))


def greedy(pond, game):
    """Where the greedy player puts the mouse: on the nearest thing it can eat."""
    head = game.player.points[0]
    prey = [hunter for hunter in game.hunters if hunter.size < game.player.size]
    if prey:
        nearest = min(prey, key=lambda hunter: math.dist(head, hunter.points[0]))
        return nearest.points[len(nearest.points) // 2]
    return min(game.food, key=lambda dot: math.dist(head, dot))


def play(pond, game, player):
    """One game from start to end. Give back how it ended, and after how many seconds."""
    game.start()
    frames = 0
    while not game.over and frames < LONGEST * FRAMES_A_SECOND:
        game.mouse = player(pond, game)
        game.play()                               # one frame of the real game, without the drawing
        frames += 1
    seconds = frames / FRAMES_A_SECOND
    if not game.over:
        return "still playing", seconds
    return ("won" if "win" in game.over else "eaten"), seconds


def main():
    games = int(sys.argv[1]) if len(sys.argv) > 1 else 40
    sys.path.insert(0, str(ASSETS))
    import pond
    game = pond.Game("rules")
    game.root.withdraw()                          # the window is made, but never shown
    random.seed(7)                                # the same food and the same wandering each run
    print(f"NEAR={pond.NEAR}  QUICK={pond.QUICK}  SWIM={pond.SWIM}  SLOW={pond.SLOW}  "
          f"BITE={pond.BITE}  FOOD={pond.FOOD}  {games} games for each player")
    for name, player in (("careful", careful), ("greedy", greedy)):
        endings = {"won": [], "eaten": [], "still playing": []}
        for _ in range(games):
            ending, seconds = play(pond, game, player)
            endings[ending].append(seconds)
        line = f"{name:8}"
        for ending, times in endings.items():
            middle = f"after {statistics.median(times):3.0f} s" if times else "           "
            line += f"   {ending} {len(times):3d} {middle}"
        print(line)
    game.root.destroy()
    return 0


if __name__ == "__main__":
    sys.exit(main())
