diff --git a/app.py b/app.py new file mode 100644 index 0000000..fb5ca52 --- /dev/null +++ b/app.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +The minigame 2048 in python with Flask Web GUI +""" +import random +from flask import Flask, render_template, jsonify, request, session +import os + +app = Flask(__name__) +app.secret_key = os.urandom(24) + +# 颜色配置 - 为不同数字设置背景色 +TILE_COLORS = { + 0: {'bg': '#cdc1b4', 'color': '#776e65'}, + 2: {'bg': '#eee4da', 'color': '#776e65'}, + 4: {'bg': '#ede0c8', 'color': '#776e65'}, + 8: {'bg': '#f2b179', 'color': '#f9f6f2'}, + 16: {'bg': '#f59563', 'color': '#f9f6f2'}, + 32: {'bg': '#f67c5f', 'color': '#f9f6f2'}, + 64: {'bg': '#f65e3b', 'color': '#f9f6f2'}, + 128: {'bg': '#edcf72', 'color': '#f9f6f2'}, + 256: {'bg': '#edcc61', 'color': '#f9f6f2'}, + 512: {'bg': '#edc850', 'color': '#f9f6f2'}, + 1024: {'bg': '#edc53f', 'color': '#f9f6f2'}, + 2048: {'bg': '#edc22e', 'color': '#f9f6f2'}, +} + +def get_tile_style(value): + """获取数字格子的样式""" + if value in TILE_COLORS: + return TILE_COLORS[value] + # 对于更大的数字,使用默认颜色 + return {'bg': '#3c3a32', 'color': '#f9f6f2'} + +def init(): + """初始化游戏矩阵""" + matrix = [0 for i in range(16)] + random_lst = random.sample(range(16), 2) + matrix[random_lst[0]] = matrix[random_lst[1]] = 2 + return matrix + +def get_random_number(matrix): + """ + 根据游戏规则获取随机数 + - 基础随机数:2, 4 + - 当矩阵中有数字超过128时,可能出现16、32 + """ + max_val = max(matrix) + + if max_val > 128: + # 有数字超过128,可以出现16、32 + weights = [50, 30, 15, 5] # 2, 4, 8, 16, 32 的权重 + choices = [2, 4, 8, 16, 32] + return random.choices(choices, weights=weights + [5])[0] + else: + # 基础随机数 2 和 4 + weights = [80, 20] + choices = [2, 4] + return random.choices(choices, weights=weights)[0] + +def move(matrix, direction): + """移动矩阵""" + matrix = list(matrix) # 创建副本 + mergedList = [] + changed = False + + if direction == 'w': # 上 + for i in range(16): + j = i + while j - 4 >= 0: + if matrix[j-4] == 0 and matrix[j] != 0: + matrix[j-4] = matrix[j] + matrix[j] = 0 + changed = True + elif matrix[j-4] == matrix[j] and matrix[j] != 0 and j not in mergedList: + matrix[j-4] *= 2 + matrix[j] = 0 + mergedList.append(j-4) + mergedList.append(j) + changed = True + j -= 4 + elif direction == 's': # 下 + for i in range(15, -1, -1): + j = i + while j + 4 < 16: + if matrix[j+4] == 0 and matrix[j] != 0: + matrix[j+4] = matrix[j] + matrix[j] = 0 + changed = True + elif matrix[j+4] == matrix[j] and matrix[j] != 0 and j not in mergedList: + matrix[j+4] *= 2 + matrix[j] = 0 + mergedList.append(j) + mergedList.append(j+4) + changed = True + j += 4 + elif direction == 'a': # 左 + for i in range(16): + j = i + while j % 4 != 0: + if matrix[j-1] == 0 and matrix[j] != 0: + matrix[j-1] = matrix[j] + matrix[j] = 0 + changed = True + elif matrix[j-1] == matrix[j] and matrix[j] != 0 and j not in mergedList: + matrix[j-1] *= 2 + matrix[j] = 0 + mergedList.append(j-1) + mergedList.append(j) + changed = True + j -= 1 + elif direction == 'd': # 右 + for i in range(15, -1, -1): + j = i + while j % 4 != 3: + if matrix[j+1] == 0 and matrix[j] != 0: + matrix[j+1] = matrix[j] + matrix[j] = 0 + changed = True + elif matrix[j+1] == matrix[j] and matrix[j] != 0 and j not in mergedList: + matrix[j+1] *= 2 + matrix[j] = 0 + mergedList.append(j) + mergedList.append(j+1) + changed = True + j += 1 + + return matrix, changed + +def insert(matrix): + """在空位置插入随机数""" + getZeroIndex = [] + for i in range(16): + if matrix[i] == 0: + getZeroIndex.append(i) + + if getZeroIndex: + randomZeroIndex = random.choice(getZeroIndex) + matrix[randomZeroIndex] = get_random_number(matrix) + return matrix + +def is_over(matrix): + """检查游戏是否结束""" + if 0 in matrix: + return False + else: + for i in range(16): + if i % 4 != 3: + if matrix[i] == matrix[i+1]: + return False + if i < 12: + if matrix[i] == matrix[i+4]: + return False + return True + +def is_win(matrix): + """检查是否获胜""" + return 2048 in matrix + +@app.route('/') +def index(): + """渲染游戏页面""" + return render_template('index.html') + +@app.route('/api/init', methods=['POST']) +def api_init(): + """初始化游戏API""" + matrix = init() + session['matrix'] = matrix + session['score'] = 0 + session['history'] = [list(matrix)] + + # 为每个格子添加样式信息 + tiles = [] + for i, val in enumerate(matrix): + style = get_tile_style(val) + tiles.append({ + 'value': val, + 'index': i, + 'bg': style['bg'], + 'color': style['color'] + }) + + return jsonify({ + 'matrix': matrix, + 'tiles': tiles, + 'score': 0, + 'game_over': False, + 'win': False + }) + +@app.route('/api/move', methods=['POST']) +def api_move(): + """移动API""" + data = request.get_json() + direction = data.get('direction') + + matrix = session.get('matrix', [0]*16) + score = session.get('score', 0) + history = session.get('history', []) + + new_matrix, changed = move(matrix, direction) + + if changed: + # 计算得分(合并产生的数值) + for i in range(16): + if new_matrix[i] != matrix[i] and matrix[i] != 0: + # 这是一个合并操作 + pass + + new_matrix = insert(new_matrix) + history.append(list(new_matrix)) + session['matrix'] = new_matrix + session['history'] = history + session['score'] = score + + # 为每个格子添加样式信息 + tiles = [] + for i, val in enumerate(new_matrix): + style = get_tile_style(val) + tiles.append({ + 'value': val, + 'index': i, + 'bg': style['bg'], + 'color': style['color'] + }) + + return jsonify({ + 'matrix': new_matrix, + 'tiles': tiles, + 'score': score, + 'changed': changed, + 'game_over': is_over(new_matrix), + 'win': is_win(new_matrix) + }) + +@app.route('/api/back', methods=['POST']) +def api_back(): + """撤销操作API""" + history = session.get('history', []) + + if len(history) > 1: + history.pop() + matrix = list(history[-1]) + session['matrix'] = matrix + session['history'] = history + + # 为每个格子添加样式信息 + tiles = [] + for i, val in enumerate(matrix): + style = get_tile_style(val) + tiles.append({ + 'value': val, + 'index': i, + 'bg': style['bg'], + 'color': style['color'] + }) + + return jsonify({ + 'matrix': matrix, + 'tiles': tiles, + 'score': session.get('score', 0), + 'success': True + }) + + return jsonify({ + 'success': False, + 'message': '无法继续撤销' + }) + +@app.route('/api/new_game', methods=['POST']) +def api_new_game(): + """新游戏API""" + return api_init() + +if __name__ == '__main__': + app.run(debug=True, host='0.0.0.0', port=5000) diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..7328de4 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,424 @@ + + + + + + 2048 游戏 + + + +
+

2048

+
+
+ 得分 + 0 +
+
+ 最高分 + 0 +
+
+
+ +
+ + +
+ +
+
+ +
+
+ +
+

游戏说明:使用方向键 移动方块

+

或者使用 W S A D 键控制方向

+

相同数字的方块相撞时会合并,目标是得到 2048

+

+ 提示:当棋盘上有数字超过128时,会出现更大的随机数(8, 16, 32)增加挑战性! +

+
+ +
+
+

+

+ +
+ + + +