<reponame>beatific-bulldogs/code-jam
"""Logic for the game.

This is used for calculationg position
and other game mechanics.
"""
import random

from .models import Apple, SnakeSegment

STARTING_SNAKE_SEGMENTS = 10

# Directions
UP = (0, -1)
DOWN = (0, 1)
LEFT = (-1, 0)
RIGHT = (1, 0)


def add_segment(segments: list, head: bool = False, id: int = 0):
    """Add a new segment to a list of segments."""
    if not (segments or head):
        # If there are no segments, it must be a head.
        head = True

    curr_index = len(segments)
    segments.append(
        SnakeSegment(
            index=curr_index,
            # TODO: Find a good position (based on previous segments if any).
            x=STARTING_SNAKE_SEGMENTS - curr_index,
            y=5,
            # TODO: Accept a player to attach the snake to.
            player=id,
            is_head=head,
        )
    )


def change_direction(
    new: str, curr_direction: tuple[int, int]
) -> tuple[int, int]:
    """Handle directions from new presses."""
    direction = curr_direction

    # New direction can't be opposite to the current one.
    if new == "up" and curr_direction != DOWN:
        direction = UP
    elif new == "down" and curr_direction != UP:
        direction = DOWN
    elif new == "right" and curr_direction != LEFT:
        direction = RIGHT
    elif new == "left" and curr_direction != RIGHT:
        direction = LEFT

    return direction


def move(direction: tuple[int, int], segments: list[SnakeSegment]):
    """Move the snake.

    This should be called each frame.
    """
    head = segments[0]
    for segment in segments[:0:-1]:
        before = segments[segment.index - 1]
        segment.x = before.x
        segment.y = before.y

    head.x += direction[0]
    head.y += direction[1]


def check_apple(segments: list, apple: Apple) -> bool:
    """Check if player has eaten apple."""
    if segments:
        head = segments[0]
        return (head.x, head.y) == (apple.x, apple.y)


def create_apple(size: tuple[int, int], segments: list) -> Apple:
    """Create apple object."""
    while True:
        coords = (
            random.randrange(2, size[0] - 2),
            random.randrange(2, size[1] - 2),
        )
        for i in segments:
            if (i.x, i.y) == coords:
                break
        break
    return Apple(x=coords[0], y=coords[1])


def has_collided_with_others(
    player: list[SnakeSegment], other: list[SnakeSegment]
) -> bool:
    """Return True if the snake has collided with other players."""
    if len(player) == 0:
        return False
    head = player[0]
    for segment in other[1:]:
        if (head.x, head.y) == (segment.x, segment.y):
            return True
    return False


def has_collided_with_self(segments: list[SnakeSegment]) -> bool:
    """Return True if the snake has collided with itself."""
    head = segments[0]
    for segment in segments[1:]:
        if (head.x, head.y) == (segment.x, segment.y):
            return True
    return False


def has_collided_with_wall(
    width: int, height: int, segments: list[SnakeSegment]
) -> bool:
    """Return True if the snake has collided with a wall."""
    head = segments[0]
    return (
        head.x <= 1 or head.x > width - 3 or head.y < 1 or head.y >= height - 2
    )

"""Logic for the game.

This is used for calculationg position
and other game mechanics.
"""
import random

from .models import Apple, SnakeSegment

STARTING_SNAKE_SEGMENTS = 10

# Directions
UP = (0, -1)
DOWN = (0, 1)
LEFT = (-1, 0)
RIGHT = (1, 0)


def add_segment(segments: list, head: bool = False, id: int = 0):
    """Add a new segment to a list of segments."""
    if not (segments or head):
        # If there are no segments, it must be a head.
        head = True

    curr_index = len(segments)
    segments.append(
        SnakeSegment(
            index=curr_index,
            # TODO: Find a good position (based on previous segments if any).
            x=STARTING_SNAKE_SEGMENTS - curr_index,
            y=5,
            # TODO: Accept a player to attach the snake to.
            player=id,
            is_head=head,
        )
    )


def change_direction(
    new: str, curr_direction: tuple[int, int]
) -> tuple[int, int]:
    """Handle directions from new presses."""
    direction = curr_direction

    # New direction can't be opposite to the current one.
    if new == "up" and curr_direction != DOWN:
        direction = UP
    elif new == "down" and curr_direction != UP:
        direction = DOWN
    elif new == "right" and curr_direction != LEFT:
        direction = RIGHT
    elif new == "left" and curr_direction != RIGHT:
        direction = LEFT

    return direction


def move(direction: tuple[int, int], segments: list[SnakeSegment]):
    """Move the snake.

    This should be called each frame.
    """
    head = segments[0]
    for segment in segments[:0:-1]:
        before = segments[segment.index - 1]
        segment.x = before.x
        segment.y = before.y

    head.x += direction[0]
    head.y += direction[1]


def check_apple(segments: list, apple: Apple) -> bool:
    """Check if player has eaten apple."""
    if segments:
        head = segments[0]
        return (head.x, head.y) == (apple.x, apple.y)


def create_apple(size: tuple[int, int], segments: list) -> Apple:
    """Create apple object."""
    while True:
        coords = (
            random.randrange(2, size[0] - 2),
            random.randrange(2, size[1] - 2),
        )
        for i in segments:
            if (i.x, i.y) == coords:
                break
        break
    return Apple(x=coords[0], y=coords[1])


def has_collided_with_others(
    player: list[SnakeSegment], other: list[SnakeSegment]
) -> bool:
    """Return True if the snake has collided with other players."""
    if len(player) == 0:
        return False
    head = player[0]
    for segment in other[1:]:
        if (head.x, head.y) == (segment.x, segment.y):
            return True
    return False


def has_collided_with_self(segments: list[SnakeSegment]) -> bool:
    """Return True if the snake has collided with itself."""
    head = segments[0]
    for segment in segments[1:]:
        if (head.x, head.y) == (segment.x, segment.y):
            return True
    return False


def has_collided_with_wall(
    width: int, height: int, segments: list[SnakeSegment]
) -> bool:
    """Return True if the snake has collided with a wall."""
    head = segments[0]
    return (
        head.x <= 1 or head.x > width - 3 or head.y < 1 or head.y >= height - 2
    )
Logic for the game.  This is used for calculationg position and other game mechanics. # Directions Add a new segment to a list of segments. # If there are no segments, it must be a head. # TODO: Find a good position (based on previous segments if any). # TODO: Accept a player to attach the snake to. Handle directions from new presses. # New direction can't be opposite to the current one. Move the snake.      This should be called each frame. Check if player has eaten apple. Create apple object. Return True if the snake has collided with other players. Return True if the snake has collided with itself. Return True if the snake has collided with a wall.