2048/src/game/game.py
2024-01-03 18:22:15 +02:00

130 lines
3.9 KiB
Python

import random
import numpy as np
from loguru import logger
from utils import Config, Direction
def play() -> None:
game = Game2048()
while True:
game.display()
move = input("Enter direction: ")
moves = {
"w": game.move_up,
"a": game.move_left,
"s": game.move_down,
"d": game.move_right,
}
if move == "q":
break
direction = moves.get(move, None)
if direction:
direction()
game.display()
class Game2048:
def __init__(self, size: int = 4):
self.size = size
self.board = np.zeros((size, size), dtype=int)
self.score = 0
self.game_over = False
self.add_random_tile()
self.add_random_tile()
def add_random_tile(self) -> None:
"""Add a random tile to the board."""
tile_value = random.choices([2, 4], weights=Config.tile.probability)[0]
tile_row_options, tile_col_options = np.nonzero(np.logical_not(self.board))
tile_loc = np.random.randint(0, len(tile_row_options))
self.board[tile_row_options[tile_loc], tile_col_options[tile_loc]] = tile_value
def move(self, direction: Direction) -> tuple[bool, int]:
match direction:
case Direction.LEFT:
return self.move_left()
case Direction.RIGHT:
return self.move_right()
case Direction.UP:
return self.move_up()
case Direction.DOWN:
return self.move_down()
def move_right(self) -> tuple[bool, int]:
self.board, moved = self._move_and_merge(self.board)
if moved:
self.add_random_tile()
return moved, self.score
def move_left(self) -> tuple[bool, int]:
board = np.rot90(self.board, 2)
board, moved = self._move_and_merge(board)
self.board = np.rot90(board, 2)
if moved:
self.add_random_tile()
return moved, self.score
def move_up(self) -> tuple[bool, int]:
board = np.rot90(self.board, -1)
board, moved = self._move_and_merge(board)
self.board = np.rot90(board, 1)
if moved:
self.add_random_tile()
return moved, self.score
def move_down(self) -> tuple[bool, int]:
board = np.rot90(self.board, 1)
board, moved = self._move_and_merge(board)
self.board = np.rot90(board, -1)
if moved:
self.add_random_tile()
return moved, self.score
def _move_and_merge(self, board: np.ndarray) -> tuple[np.ndarray, bool]:
board, has_pushed = self._push_board_right(board)
board, has_merged = self._merge(board)
board, _ = self._push_board_right(board)
return board, has_pushed or has_merged
def _merge(self, board: np.ndarray) -> tuple[np.ndarray, bool]:
done = False
for row in range(self.size):
for col in range(self.size - 1, 0, -1):
if board[row, col] == board[row, col - 1] and board[row, col] != 0:
board[row, col] *= 2
self.score += board[row, col]
board[row, col - 1] = 0
done = True
return board, done
def display(self) -> None:
print(f"Score: {self.score}")
for row in self.board:
for val in row:
print(f"{val:^3}" if val != 0 else " ", end="")
print()
def _push_board_right(self, board: np.ndarray) -> tuple[np.ndarray, bool]:
new_board = np.zeros((self.size, self.size), dtype=int)
done = False
for row in range(self.size):
count = self.size - 1
for col in range(self.size - 1, -1, -1):
if board[row, col] != 0:
new_board[row, count] = board[row, col]
if col != count:
done = True
count -= 1
return new_board, done