Mystery Game #11
← Back to Games List
// 2048 Game Implementation
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Game settings
const gridSize = 4;
const cellSize = 100;
const padding = 10;
canvas.width = gridSize * (cellSize + padding) + padding;
canvas.height = canvas.width;
// Colors for different numbers
const colors = {
2: '#eee4da',
4: '#ede0c8',
8: '#f2b179',
16: '#f59563',
32: '#f67c5f',
64: '#f65e3b',
128: '#edcf72',
256: '#edcc61',
512: '#edc850',
1024: '#edc53f',
2048: '#edc22e'
};
// Game state
let grid = [];
let score = 0;
let gameOver = false;
let won = false;
// Initialize grid
function initGrid() {
grid = Array(gridSize).fill().map(() => Array(gridSize).fill(0));
addNewTile();
addNewTile();
}
// Add new tile (2 or 4)
function addNewTile() {
const emptyCells = [];
for (let i = 0; i < gridSize; i++) {
for (let j = 0; j < gridSize; j++) {
if (grid[i][j] === 0) {
emptyCells.push({x: i, y: j});
}
}
}
if (emptyCells.length > 0) {
const cell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
grid[cell.x][cell.y] = Math.random() < 0.9 ? 2 : 4;
}
}
// Move tiles in a direction
function moveTiles(direction) {
let moved = false;
const tempGrid = grid.map(row => [...row]);
// Rotate grid to simplify movement logic
for (let i = 0; i < direction; i++) {
grid = rotateGrid(grid);
}
// Move and merge tiles
for (let i = 0; i < gridSize; i++) {
let row = grid[i].filter(cell => cell !== 0);
for (let j = 0; j < row.length - 1; j++) {
if (row[j] === row[j + 1]) {
row[j] *= 2;
score += row[j];
row.splice(j + 1, 1);
if (row[j] === 2048 && !won) {
won = true;
}
}
}
row = row.concat(Array(gridSize - row.length).fill(0));
grid[i] = row;
}
// Rotate back
for (let i = 0; i < (4 - direction) % 4; i++) {
grid = rotateGrid(grid);
}
// Check if grid changed
for (let i = 0; i < gridSize; i++) {
for (let j = 0; j < gridSize; j++) {
if (grid[i][j] !== tempGrid[i][j]) {
moved = true;
}
}
}
return moved;
}
// Rotate grid clockwise
function rotateGrid(grid) {
const newGrid = Array(gridSize).fill().map(() => Array(gridSize).fill(0));
for (let i = 0; i < gridSize; i++) {
for (let j = 0; j < gridSize; j++) {
newGrid[j][gridSize - 1 - i] = grid[i][j];
}
}
return newGrid;
}
// Check if game is over
function checkGameOver() {
// Check for empty cells
for (let i = 0; i < gridSize; i++) {
for (let j = 0; j < gridSize; j++) {
if (grid[i][j] === 0) return false;
}
}
// Check for possible merges
for (let i = 0; i < gridSize; i++) {
for (let j = 0; j < gridSize; j++) {
if (j < gridSize - 1 && grid[i][j] === grid[i][j + 1]) return false;
if (i < gridSize - 1 && grid[i][j] === grid[i + 1][j]) return false;
}
}
return true;
}
// Handle keyboard input
document.addEventListener('keydown', (e) => {
if (gameOver || won) {
if (e.key === 'Enter') {
grid = Array(gridSize).fill().map(() => Array(gridSize).fill(0));
score = 0;
gameOver = false;
won = false;
initGrid();
}
return;
}
let moved = false;
switch(e.key) {
case 'ArrowLeft':
moved = moveTiles(3); // Left movement
break;
case 'ArrowRight':
moved = moveTiles(1); // Right movement
break;
case 'ArrowUp':
moved = moveTiles(0); // Up movement
break;
case 'ArrowDown':
moved = moveTiles(2); // Down movement
break;
}
if (moved) {
addNewTile();
if (checkGameOver()) {
gameOver = true;
}
}
});
function draw() {
// Clear canvas
ctx.fillStyle = '#bbada0';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw grid
for (let i = 0; i < gridSize; i++) {
for (let j = 0; j < gridSize; j++) {
const value = grid[i][j];
const x = j * (cellSize + padding) + padding;
const y = i * (cellSize + padding) + padding;
// Draw cell background
ctx.fillStyle = value === 0 ? '#cdc1b4' : colors[value] || '#edc22e';
ctx.fillRect(x, y, cellSize, cellSize);
// Draw number
if (value !== 0) {
ctx.fillStyle = value <= 4 ? '#776e65' : '#f9f6f2';
ctx.font = value >= 1000 ? '35px Arial' : '40px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(value, x + cellSize/2, y + cellSize/2);
}
}
}
// Draw score
ctx.fillStyle = '#776e65';
ctx.font = '20px Arial';
ctx.textAlign = 'left';
ctx.fillText('Score: ' + score, padding, canvas.height - padding);
// Draw game over or win message
if (gameOver || won) {
ctx.fillStyle = 'rgba(238, 228, 218, 0.8)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#776e65';
ctx.font = '40px Arial';
ctx.textAlign = 'center';
ctx.fillText(won ? 'You Win!' : 'Game Over!', canvas.width/2, canvas.height/2 - 20);
ctx.font = '20px Arial';
ctx.fillText('Score: ' + score, canvas.width/2, canvas.height/2 + 20);
ctx.fillText('Press Enter to Play Again', canvas.width/2, canvas.height/2 + 50);
}
}
// Start game
initGrid();
gameLoop();
# 2048 Game Implementation
import pygame
import random
from dataclasses import dataclass
from typing import List, Dict, Tuple
import numpy as np
@dataclass
class Colors:
GRID_COLOR: Tuple[int, int, int] = (187, 173, 160) # #bbada0
EMPTY_CELL: Tuple[int, int, int] = (205, 193, 180) # #cdc1b4
TEXT_DARK: Tuple[int, int, int] = (119, 110, 101) # #776e65
TEXT_LIGHT: Tuple[int, int, int] = (249, 246, 242) # #f9f6f2
# Tile colors
TILE_COLORS: Dict[int, Tuple[int, int, int]] = {
2: (238, 228, 218), # #eee4da
4: (237, 224, 200), # #ede0c8
8: (242, 177, 121), # #f2b179
16: (245, 149, 99), # #f59563
32: (246, 124, 95), # #f67c5f
64: (246, 94, 59), # #f65e3b
128: (237, 207, 114), # #edcf72
256: (237, 204, 97), # #edcc61
512: (237, 200, 80), # #edc850
1024: (237, 197, 63), # #edc53f
2048: (237, 194, 46) # #edc22e
}
class Game2048:
def __init__(self):
pygame.init()
# Game settings
self.grid_size = 4
self.cell_size = 100
self.padding = 10
self.width = self.grid_size * (self.cell_size + self.padding) + self.padding
self.height = self.width
# Initialize display
self.screen = pygame.display.set_mode((self.width, self.height))
pygame.display.set_caption('2048')
self.clock = pygame.time.Clock()
# Colors
self.colors = Colors()
# Game state
self.grid = np.zeros((self.grid_size, self.grid_size), dtype=int)
self.score = 0
self.game_over = False
self.won = False
# Initialize game
self.init_grid()
def init_grid(self):
"""Initialize grid with two random tiles"""
self.add_new_tile()
self.add_new_tile()
def add_new_tile(self):
"""Add a new tile (2 or 4) to a random empty cell"""
empty_cells = list(zip(*np.where(self.grid == 0)))
if empty_cells:
i, j = random.choice(empty_cells)
self.grid[i][j] = 2 if random.random() < 0.9 else 4
def move_tiles(self, direction: int) -> bool:
"""Move tiles in specified direction (0:up, 1:right, 2:down, 3:left)"""
# Save original grid for comparison
original = self.grid.copy()
# Rotate grid to handle all directions uniformly
self.grid = np.rot90(self.grid, direction)
# Process each row
for i in range(self.grid_size):
# Remove zeros and get non-empty tiles
row = self.grid[i][self.grid[i] != 0]
# Merge adjacent equal tiles
for j in range(len(row) - 1):
if row[j] == row[j + 1]:
row[j] *= 2
self.score += row[j]
row = np.delete(row, j + 1)
if row[j] == 2048:
self.won = True
break
# Pad with zeros
new_row = np.zeros(self.grid_size, dtype=int)
new_row[:len(row)] = row
self.grid[i] = new_row
# Rotate back
self.grid = np.rot90(self.grid, -direction)
# Check if grid changed
return not np.array_equal(original, self.grid)
def check_game_over(self) -> bool:
"""Check if no moves are possible"""
if np.any(self.grid == 0):
return False
# Check for possible merges
for i in range(self.grid_size):
for j in range(self.grid_size):
value = self.grid[i][j]
if (j < self.grid_size - 1 and value == self.grid[i][j + 1]) or \
(i < self.grid_size - 1 and value == self.grid[i + 1][j]):
return False
return True
def handle_input(self) -> bool:
"""Handle keyboard input"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
return False
if event.type == pygame.KEYDOWN:
if self.game_over or self.won:
if event.key == pygame.K_RETURN:
self.grid = np.zeros((self.grid_size, self.grid_size), dtype=int)
self.score = 0
self.game_over = False
self.won = False
self.init_grid()
else:
moved = False
if event.key == pygame.K_LEFT:
moved = self.move_tiles(3)
elif event.key == pygame.K_RIGHT:
moved = self.move_tiles(1)
elif event.key == pygame.K_UP:
moved = self.move_tiles(0)
elif event.key == pygame.K_DOWN:
moved = self.move_tiles(2)
if moved:
self.add_new_tile()
if self.check_game_over():
self.game_over = True
return True
def draw(self):
"""Draw the game state"""
# Clear screen with background color
self.screen.fill(self.colors.GRID_COLOR)
# Draw grid
for i in range(self.grid_size):
for j in range(self.grid_size):
value = self.grid[i][j]
x = j * (self.cell_size + self.padding) + self.padding
y = i * (self.cell_size + self.padding) + self.padding
# Draw cell background
color = self.colors.EMPTY_CELL if value == 0 else \
self.colors.TILE_COLORS.get(value, self.colors.TILE_COLORS[2048])
pygame.draw.rect(self.screen, color,
(x, y, self.cell_size, self.cell_size))
# Draw number
if value != 0:
font_size = 35 if value >= 1000 else 40
font = pygame.font.Font(None, font_size)
text_color = self.colors.TEXT_DARK if value <= 4 else self.colors.TEXT_LIGHT
text = font.render(str(value), True, text_color)
text_rect = text.get_rect(center=(x + self.cell_size/2,
y + self.cell_size/2))
self.screen.blit(text, text_rect)
# Draw score
font = pygame.font.Font(None, 36)
score_text = font.render(f'Score: {self.score}', True, self.colors.TEXT_DARK)
self.screen.blit(score_text, (self.padding, self.height - self.padding - 20))
# Draw game over or win message
if self.game_over or self.won:
overlay = pygame.Surface((self.width, self.height))
overlay.fill((238, 228, 218))
overlay.set_alpha(204) # 80% opacity
self.screen.blit(overlay, (0, 0))
font = pygame.font.Font(None, 64)
message = 'You Win!' if self.won else 'Game Over!'
text = font.render(message, True, self.colors.TEXT_DARK)
text_rect = text.get_rect(center=(self.width/2, self.height/2 - 20))
self.screen.blit(text, text_rect)
font = pygame.font.Font(None, 36)
score = font.render(f'Score: {self.score}', True, self.colors.TEXT_DARK)
score_rect = score.get_rect(center=(self.width/2, self.height/2 + 20))
self.screen.blit(score, score_rect)
play_again = font.render('Press Enter to Play Again',
True, self.colors.TEXT_DARK)
play_again_rect = play_again.get_rect(
center=(self.width/2, self.height/2 + 50))
self.screen.blit(play_again, play_again_rect)
pygame.display.flip()
def run(self):
"""Main game loop"""
running = True
while running:
running = self.handle_input()
self.draw()
self.clock.tick(60)
pygame.quit()
if __name__ == '__main__':
game = Game2048()
game.run()
Play Game