Untitled
unknown
python
a year ago
11 kB
2
Indexable
Never
import random import time from collections import deque from enum import Enum from typing import Deque from typing import Optional try: import curses class Window: def __init__(self) -> None: self._window = curses.initscr() self._window.keypad(True) self._window.nodelay(True) self._window.clear() def refresh(self) -> None: self._window.refresh() def getch(self) -> int: return self._window.getch() def addstr(self, y: int, x: int, str: str, attr: int) -> None: self._window.addstr(y, x, str, attr) except ImportError: try: import unicurses as curses class Window: def __init__(self) -> None: self._window = curses.initscr() curses.keypad(self._window, True) curses.nodelay(self._window, True) curses.wclear(self._window) def refresh(self) -> None: curses.wrefresh(self._window) def getch(self) -> int: return curses.wgetch(self._window) def addstr(self, y: int, x: int, str: str, attr: int) -> None: curses.wbkgdset(self._window, attr) curses.wmove(self._window, y, x) curses.waddstr(self._window, str) except ImportError: print("\n setup unicurses : pip install Uni-Curses \n") quit() class Direction(Enum): Left = 1 Up = 2 Down = 3 Right = 4 class Cell: def __init__(self, x: int, y: int) -> None: self.x = x self.y = y class Navigator: def get_direction(self) -> Optional[Direction]: return None class Console(Navigator): def __init__(self) -> None: self._window = Window() curses.curs_set(0) curses.noecho() curses.cbreak() curses.start_color() def init(self, max_x: int, max_y: int, color: int) -> None: self._max_x = max_x self._max_y = max_y self._color_map = [ [color for j in range(max_y + 1)] for i in range(max_x)] for i in range(max_x): for j in range(max_y): self._refresh_cell(Cell(i, j)) def set_collor(self, cell: Cell, color: int) -> None: self._color_map[cell.x][cell.y] = color self._refresh_cell(cell) def print_status(self, text: str) -> None: color_pair = self._get_collor_pair( curses.COLOR_WHITE, curses.COLOR_BLACK) self._window.addstr(self._max_y // 2 + 1, 0, text, color_pair) def refresh(self) -> None: self._window.refresh() def get_direction(self) -> Optional[Direction]: c = self._window.getch() if c == curses.ERR: return None if c == curses.KEY_UP: return Direction.Up if c == curses.KEY_DOWN: return Direction.Down if c == curses.KEY_LEFT: return Direction.Left if c == curses.KEY_RIGHT: return Direction.Right return None def _get_collor_pair(self, foreground: int, background: int) -> int: pair = foreground * 8 + background curses.init_pair(pair, foreground, background) return curses.color_pair(pair) def _refresh_cell(self, cell: Cell) -> None: foreground = self._color_map[cell.x][(cell.y // 2) * 2] background = self._color_map[cell.x][(cell.y // 2) * 2 + 1] color_pair = self._get_collor_pair(foreground, background) self._window.addstr(cell.y // 2 + 1, cell.x, "▀", color_pair) class CellType(Enum): FREE = curses.COLOR_BLUE WALL = curses.COLOR_BLACK APPLE = curses.COLOR_RED POISON = curses.COLOR_GREEN SNAKE = curses.COLOR_WHITE class Field: def __init__(self, max_x: int, max_y: int, console: Console) -> None: self.max_x = max_x self.max_y = max_y self._console = console self._console.init(max_x, max_y, CellType.FREE.value) self._data = [[CellType.FREE for j in range(max_y)] for i in range(max_x)] self._free_cells = list() for x in range(max_x): for y in range(max_y): self._free_cells.append(Cell(x, y)) def get_cell_type(self, cell: Cell) -> CellType: # if cell.x >= len(self._data) or cell.x < 0: # cell.x = cell.x % len(self._data) # return CellType.WALL # if cell.y >= len(self._data[cell.x]) or cell.y < 0: # cell.y = cell.y % len(self._data[cell.x]) # return CellType.WALL return self._data[cell.x][cell.y] def set_cell_type(self, cell: Cell, type: CellType) -> None: if type == CellType.SNAKE: for i in range(len(self._free_cells)-1, -1, -1): if self._free_cells[i].x == cell.x and self._free_cells[i].y == cell.y: self._free_cells.pop(i) else: self._free_cells.append(cell) self._data[cell.x][cell.y] = type self._console.set_collor(cell, type.value) def set_random_cell_type(self, cell_type) -> None: # cell_type is not FREE # x = random.randint(0, len(self._data) - 1) # y = random.randint(0, len(self._data[x]) - 1) cell = random.choice(self._free_cells) self.set_cell_type(cell, cell_type) def generate_apple(self) -> None: self.set_random_cell_type(CellType.APPLE) def generate_poison(self) -> None: self.set_random_cell_type(CellType.POISON) class Snake: def __init__(self, cell: Cell, direction: Direction, field: Field): self._direction = direction self._field = field self._body: Deque[Cell] = deque() self._occupate_cell(cell) self._apples_eaten = 0 self._poisons_eaten = 0 def apples_eaten(self): return self._apples_eaten def poisons_eaten(self): return self._poisons_eaten def change_direction(self, new_direction: Direction) -> bool: self._direction = new_direction return True def move(self) -> bool: new_head = self._get_next_cell() cell_type = self._field.get_cell_type(new_head) if cell_type == CellType.WALL: return False if cell_type == CellType.SNAKE: return False if cell_type == CellType.APPLE: self._apples_eaten += 1 self._occupate_cell(new_head) if cell_type == CellType.POISON: self._poisons_eaten += 1 self._field.set_cell_type(new_head, CellType.FREE) self._deoccupate_tail() if self.len() >= self._field.max_x * self._field.max_y: print("Victory!") exit(1) if cell_type == CellType.APPLE or cell_type == CellType.POISON: if random.randint(0, 2) == 5: self._field.generate_poison() else: self._field.generate_apple() return True self._occupate_cell(new_head) self._release_last_cell() return True def head(self) -> Cell: return self._body[-1] def len(self) -> int: return len(self._body) def _occupate_cell(self, cell: Cell) -> None: self._body.append(cell) self._field.set_cell_type(cell, CellType.SNAKE) def _deoccupate_tail(self) -> None: if len(self._body) <= 1: exit(1) deleted_cell = self._body.popleft() self._field.set_cell_type(deleted_cell, CellType.FREE) def _release_last_cell(self) -> None: tail = self._body.popleft() self._field.set_cell_type(tail, CellType.FREE) def _get_next_cell(self) -> Cell: head = self.head() cell = None if self._direction == Direction.Up: cell = Cell(head.x, head.y - 1) if self._direction == Direction.Down: cell = Cell(head.x, head.y + 1) if self._direction == Direction.Left: cell = Cell(head.x - 1, head.y) if self._direction == Direction.Right: cell = Cell(head.x + 1, head.y) if cell.x >= self._field.max_x or cell.x < 0: cell.x = cell.x % self._field.max_x # return CellType.WALL if cell.y >= self._field.max_y or cell.y < 0: cell.y = cell.y % self._field.max_y # return CellType.WALL return cell class Autopilot(Navigator): def __init__(self, field: Field, snake: Snake): self._snake = snake self._field = field self._direction = Direction.Right self._left_len = 1 def get_direction(self) -> Direction: # if self._left_len > 0: # self._left_len -= 1 # return self._direction # else: # # last_direction = self._direction # opposite_dirs = { # Direction.Up: Direction.Down, # Direction.Down: Direction.Up, # Direction.Left: Direction.Right, # Direction.Right: Direction.Left # } # choices = [Direction.Up, Direction.Down, Direction.Left, Direction.Right] # choices.remove(opposite_dirs[self._direction]) # self._direction = random.choice(choices) # self._left_len = random.randint(2, 5) # return self._direction head = self._snake.head() if head.y == 0: if head.x % 2 == 0: return Direction.Right else: return Direction.Down elif head.y < self._field.max_y - 2: if head.x % 2 == 0: return Direction.Up else: return Direction.Down elif head.y == self._field.max_y - 2: if head.x == self._field.max_x - 1: return Direction.Down elif head.x % 2 == 0: return Direction.Up else: return Direction.Right else: if head.x == 0: return Direction.Up else: return Direction.Left MAX_X = 10 MAX_Y = 10 AUTOPILOT = True FRAME_DUARATION = 0.3 if not AUTOPILOT else 0.03 console = Console() field = Field(MAX_X, MAX_Y, console) field.generate_apple() snake = Snake(Cell(MAX_X // 2, MAX_Y // 2), Direction.Right, field) navigator: Navigator = Autopilot(field, snake) if AUTOPILOT else console while snake.move(): time.sleep(FRAME_DUARATION) direction = navigator.get_direction() if direction: snake.change_direction(direction) console.print_status("len : {}, apples : {}, poisons : {}".format( snake.len(), snake.apples_eaten(), snake.poisons_eaten())) console.refresh() print("\n Game over... \n")