python
import random
import sys
import pygame
GRID_SIZE = 4
TILE_SIZE = 105
GAP = 12
BOARD_SIZE = GRID_SIZE * TILE_SIZE + (GRID_SIZE + 1) * GAP
SIDE_PANEL = 230
WIDTH = BOARD_SIZE + SIDE_PANEL
HEIGHT = BOARD_SIZE
FPS = 60
MOVE_ANIMATION_MS = 120
BG_COLOR = (250, 248, 239)
BOARD_COLOR = (187, 173, 160)
EMPTY_TILE_COLOR = (205, 193, 180)
TEXT_DARK = (119, 110, 101)
TEXT_LIGHT = (249, 246, 242)
PANEL_TEXT = (90, 82, 74)
BUTTON_COLOR = (143, 122, 102)
BUTTON_HOVER = (124, 102, 82)
CHEAT_ON_COLOR = (76, 145, 112)
CHEAT_ON_HOVER = (58, 126, 95)
OVERLAY_COLOR = (250, 248, 239, 215)
TILE_COLORS = {
2: (238, 228, 218),
4: (237, 224, 200),
8: (242, 177, 121),
16: (245, 149, 99),
32: (246, 124, 95),
64: (246, 94, 59),
128: (237, 207, 114),
256: (237, 204, 97),
512: (237, 200, 80),
1024: (237, 197, 63),
2048: (237, 194, 46),
}
def make_board():
board = [[0 for _ in range(GRID_SIZE)] for _ in range(GRID_SIZE)]
add_random_tile(board)
add_random_tile(board)
return board
def add_random_tile(board):
empty_cells = [
(row, col)
for row in range(GRID_SIZE)
for col in range(GRID_SIZE)
if board[row][col] == 0
]
if not empty_cells:
return
row, col = random.choice(empty_cells)
board[row][col] = 4 if random.random() < 0.1 else 2
def get_merged_value(first, second, cheat_mode=False):
if cheat_mode:
return max(first, second) * 2
if first == second:
return first * 2
return None
def compress_and_merge(line, cheat_mode=False):
values = [value for value in line if value != 0]
merged = []
score_gain = 0
index = 0
while index < len(values):
if index + 1 < len(values):
new_value = get_merged_value(values[index], values[index + 1], cheat_mode)
else:
new_value = None
if new_value is not None:
merged.append(new_value)
score_gain += new_value
index += 2
else:
merged.append(values[index])
index += 1
merged.extend([0] * (GRID_SIZE - len(merged)))
return merged, score_gain
def move_board(board, direction, cheat_mode=False):
new_board = [row[:] for row in board]
score_gain = 0
if direction == "left":
for row in range(GRID_SIZE):
new_board[row], gained = compress_and_merge(new_board[row], cheat_mode)
score_gain += gained
elif direction == "right":
for row in range(GRID_SIZE):
merged, gained = compress_and_merge(list(reversed(new_board[row])), cheat_mode)
new_board[row] = list(reversed(merged))
score_gain += gained
elif direction == "up":
for col in range(GRID_SIZE):
column = [new_board[row][col] for row in range(GRID_SIZE)]
merged, gained = compress_and_merge(column, cheat_mode)
score_gain += gained
for row in range(GRID_SIZE):
new_board[row][col] = merged[row]
elif direction == "down":
for col in range(GRID_SIZE):
column = [new_board[row][col] for row in range(GRID_SIZE)]
merged, gained = compress_and_merge(list(reversed(column)), cheat_mode)
merged = list(reversed(merged))
score_gain += gained
for row in range(GRID_SIZE):
new_board[row][col] = merged[row]
moved = new_board != board
return new_board, score_gain, moved
def collapse_line_with_animations(values, positions, cheat_mode=False):
nonzero_tiles = [
(index, value)
for index, value in enumerate(values)
if value != 0
]
new_values = [0] * GRID_SIZE
animations = []
score_gain = 0
source_index = 0
target_index = 0
while source_index < len(nonzero_tiles):
start_slot, value = nonzero_tiles[source_index]
start_pos = positions[start_slot]
end_pos = positions[target_index]
if source_index + 1 < len(nonzero_tiles):
second_slot, second_value = nonzero_tiles[source_index + 1]
new_value = get_merged_value(value, second_value, cheat_mode)
else:
second_slot = None
new_value = None
if new_value is not None:
new_values[target_index] = new_value
score_gain += new_value
animations.append({"value": value, "start": start_pos, "end": end_pos})
animations.append({"value": second_value, "start": positions[second_slot], "end": end_pos})
source_index += 2
else:
new_values[target_index] = value
animations.append({"value": value, "start": start_pos, "end": end_pos})
source_index += 1
target_index += 1
return new_values, score_gain, animations
def move_board_with_animations(board, direction, cheat_mode=False):
new_board = [[0 for _ in range(GRID_SIZE)] for _ in range(GRID_SIZE)]
animations = []
score_gain = 0
if direction == "left":
lines = [[(row, col) for col in range(GRID_SIZE)] for row in range(GRID_SIZE)]
elif direction == "right":
lines = [[(row, col) for col in range(GRID_SIZE - 1, -1, -1)] for row in range(GRID_SIZE)]
elif direction == "up":
lines = [[(row, col) for row in range(GRID_SIZE)] for col in range(GRID_SIZE)]
else:
lines = [[(row, col) for row in range(GRID_SIZE - 1, -1, -1)] for col in range(GRID_SIZE)]
for positions in lines:
values = [board[row][col] for row, col in positions]
new_values, gained, line_animations = collapse_line_with_animations(
values,
positions,
cheat_mode,
)
score_gain += gained
animations.extend(line_animations)
for index, value in enumerate(new_values):
row, col = positions[index]
new_board[row][col] = value
moved = new_board != board
return new_board, score_gain, moved, animations
def can_move(board, cheat_mode=False):
if any(0 in row for row in board):
return True
for row in range(GRID_SIZE):
for col in range(GRID_SIZE):
value = board[row][col]
if col + 1 < GRID_SIZE and (
cheat_mode or value == board[row][col + 1]
):
return True
if row + 1 < GRID_SIZE and (
cheat_mode or value == board[row + 1][col]
):
return True
return False
def has_won(board):
return any(value >= 2048 for row in board for value in row)
def get_font(size, bold=False):
return pygame.font.SysFont("arial", size, bold=bold)
def draw_text(surface, text, font, color, center):
image = font.render(text, True, color)
rect = image.get_rect(center=center)
surface.blit(image, rect)
def ease_out_cubic(progress):
return 1 - (1 - progress) ** 3
def draw_button(surface, rect, text, mouse_pos, active=False):
base_color = CHEAT_ON_COLOR if active else BUTTON_COLOR
hover_color = CHEAT_ON_HOVER if active else BUTTON_HOVER
color = hover_color if rect.collidepoint(mouse_pos) else base_color
pygame.draw.rect(surface, color, rect, border_radius=6)
draw_text(surface, text, get_font(22, bold=True), TEXT_LIGHT, rect.center)
def tile_rect(row, col):
x = GAP + col * (TILE_SIZE + GAP)
y = GAP + row * (TILE_SIZE + GAP)
return pygame.Rect(x, y, TILE_SIZE, TILE_SIZE)
def draw_tile(surface, value, rect):
color = TILE_COLORS.get(value, (60, 58, 50))
pygame.draw.rect(surface, color, rect, border_radius=6)
font_size = 52 if value < 100 else 44 if value < 1000 else 36
font = get_font(font_size, bold=True)
text_color = TEXT_DARK if value in (2, 4) else TEXT_LIGHT
draw_text(surface, str(value), font, text_color, rect.center)
def draw_board_background(surface):
board_rect = pygame.Rect(0, 0, BOARD_SIZE, BOARD_SIZE)
pygame.draw.rect(surface, BOARD_COLOR, board_rect, border_radius=8)
for row in range(GRID_SIZE):
for col in range(GRID_SIZE):
rect = tile_rect(row, col)
pygame.draw.rect(surface, EMPTY_TILE_COLOR, rect, border_radius=6)
def draw_board(surface, board):
draw_board_background(surface)
for row in range(GRID_SIZE):
for col in range(GRID_SIZE):
value = board[row][col]
if value:
draw_tile(surface, value, tile_rect(row, col))
def draw_moving_tiles(surface, animations, progress):
eased = ease_out_cubic(progress)
for animation in animations:
start_row, start_col = animation["start"]
end_row, end_col = animation["end"]
start_rect = tile_rect(start_row, start_col)
end_rect = tile_rect(end_row, end_col)
x = start_rect.x + (end_rect.x - start_rect.x) * eased
y = start_rect.y + (end_rect.y - start_rect.y) * eased
rect = pygame.Rect(round(x), round(y), TILE_SIZE, TILE_SIZE)
draw_tile(surface, animation["value"], rect)
def draw_panel(surface, score, best_score, mouse_pos, new_game_rect, cheat_rect, cheat_mode):
panel_x = BOARD_SIZE
pygame.draw.rect(surface, BG_COLOR, (panel_x, 0, SIDE_PANEL, HEIGHT))
draw_text(surface, "2048", get_font(62, bold=True), PANEL_TEXT, (panel_x + 115, 62))
score_rect = pygame.Rect(panel_x + 30, 112, 170, 64)
pygame.draw.rect(surface, BOARD_COLOR, score_rect, border_radius=6)
draw_text(surface, "SCORE", get_font(17, bold=True), TEXT_LIGHT, (score_rect.centerx, score_rect.y + 20))
draw_text(surface, str(score), get_font(28, bold=True), TEXT_LIGHT, (score_rect.centerx, score_rect.y + 48))
best_rect = pygame.Rect(panel_x + 30, 190, 170, 64)
pygame.draw.rect(surface, BOARD_COLOR, best_rect, border_radius=6)
draw_text(surface, "BEST", get_font(17, bold=True), TEXT_LIGHT, (best_rect.centerx, best_rect.y + 20))
draw_text(surface, str(best_score), get_font(28, bold=True), TEXT_LIGHT, (best_rect.centerx, best_rect.y + 48))
draw_button(surface, new_game_rect, "New Game", mouse_pos)
draw_button(surface, cheat_rect, f"Cheat: {'ON' if cheat_mode else 'OFF'}", mouse_pos, cheat_mode)
help_lines = ["Arrow keys / WASD", "R: restart", "Esc: quit"]
for index, line in enumerate(help_lines):
draw_text(surface, line, get_font(19), PANEL_TEXT, (panel_x + 115, 420 + index * 26))
def draw_overlay(surface, title, subtitle):
overlay = pygame.Surface((BOARD_SIZE, BOARD_SIZE), pygame.SRCALPHA)
overlay.fill(OVERLAY_COLOR)
surface.blit(overlay, (0, 0))
draw_text(surface, title, get_font(56, bold=True), PANEL_TEXT, (BOARD_SIZE // 2, BOARD_SIZE // 2 - 45))
draw_text(surface, subtitle, get_font(25, bold=True), PANEL_TEXT, (BOARD_SIZE // 2, BOARD_SIZE // 2 + 15))
def main():
pygame.init()
pygame.display.set_caption("Pygame 2048")
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
board = make_board()
score = 0
best_score = 0
game_over = False
game_won = False
cheat_mode = False
animation_start_ms = None
animation_tiles = []
pending_board = None
pending_score_gain = 0
new_game_rect = pygame.Rect(BOARD_SIZE + 30, 280, 170, 52)
cheat_rect = pygame.Rect(BOARD_SIZE + 30, 344, 170, 52)
key_to_direction = {
pygame.K_LEFT: "left",
pygame.K_RIGHT: "right",
pygame.K_UP: "up",
pygame.K_DOWN: "down",
}
letter_to_direction = {
"a": "left",
"d": "right",
"w": "up",
"s": "down",
}
while True:
mouse_pos = pygame.mouse.get_pos()
if animation_start_ms is not None:
elapsed = pygame.time.get_ticks() - animation_start_ms
if elapsed >= MOVE_ANIMATION_MS:
board = pending_board
score += pending_score_gain
best_score = max(best_score, score)
add_random_tile(board)
game_won = has_won(board)
game_over = not can_move(board, cheat_mode)
animation_start_ms = None
animation_tiles = []
pending_board = None
pending_score_gain = 0
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
if new_game_rect.collidepoint(event.pos):
board = make_board()
score = 0
game_over = False
game_won = False
animation_start_ms = None
animation_tiles = []
pending_board = None
pending_score_gain = 0
elif cheat_rect.collidepoint(event.pos):
cheat_mode = not cheat_mode
if not game_won:
game_over = not can_move(board, cheat_mode)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
if event.key == pygame.K_r:
board = make_board()
score = 0
game_over = False
game_won = False
animation_start_ms = None
animation_tiles = []
pending_board = None
pending_score_gain = 0
continue
direction = key_to_direction.get(event.key)
if direction is None:
direction = letter_to_direction.get(event.unicode.lower())
if (
direction is not None
and not game_over
and not game_won
and animation_start_ms is None
):
next_board, gained, moved, animations = move_board_with_animations(
board,
direction,
cheat_mode,
)
if moved:
animation_start_ms = pygame.time.get_ticks()
animation_tiles = animations
pending_board = next_board
pending_score_gain = gained
screen.fill(BG_COLOR)
if animation_start_ms is None:
draw_board(screen, board)
else:
elapsed = pygame.time.get_ticks() - animation_start_ms
progress = min(1, elapsed / MOVE_ANIMATION_MS)
draw_board_background(screen)
draw_moving_tiles(screen, animation_tiles, progress)
draw_panel(
screen,
score,
best_score,
mouse_pos,
new_game_rect,
cheat_rect,
cheat_mode,
)
if animation_start_ms is None and game_won:
draw_overlay(screen, "You Win!", "Press R or New Game")
elif animation_start_ms is None and game_over:
draw_overlay(screen, "Game Over", "Press R or New Game")
pygame.display.flip()
clock.tick(FPS)
if __name__ == "__main__":
main()

记得使用英文的键盘输入
我一直过不去 自己写了个作弊模式把cheat打开作弊