6-7 Deque(25 point(s))

双向队列数据结构实现
本文介绍了一种双向队列(deque)的数据结构实现方法,使用双向链表作为底层存储结构,支持在队首和队尾进行插入和删除操作,所有操作的时间复杂度均为O(1)。
6-7 Deque(25 point(s))

A "deque" is a data structure consisting of a list of items, on which the following operations are possible:

  • Push(X,D): Insert item X on the front end of deque D.
  • Pop(D): Remove the front item from deque D and return it.
  • Inject(X,D): Insert item X on the rear end of deque D.
  • Eject(D): Remove the rear item from deque D and return it.Write routines to support the deque that take O(1) time per operation.

Format of functions:

Deque CreateDeque();
int Push( ElementType X, Deque D );
ElementType Pop( Deque D );
int Inject( ElementType X, Deque D );
ElementType Eject( Deque D );

where Deque is defined as the following:

typedef struct Node *PtrToNode;
struct Node {
    ElementType Element;
    PtrToNode Next, Last;
};
typedef struct DequeRecord *Deque;
struct DequeRecord {
    PtrToNode Front, Rear;
};

Here the deque is implemented by a doubly linked list with a header. Front and Rear point to the two ends of the deque respectively. Front always points to the header. The deque is empty when Front and Rear both point to the same dummy header.Note: Push and Inject are supposed to return 1 if the operations can be done successfully, or 0 if fail. If the deque is empty, Pop and Eject must return ERROR which is defined by the judge program.

Sample program of judge:

#include <stdio.h>
#include <stdlib.h>

#define ElementType int
#define ERROR 1e5
typedef enum { push, pop, inject, eject, end } Operation;

typedef struct Node *PtrToNode;
struct Node {
    ElementType Element;
    PtrToNode Next, Last;
};
typedef struct DequeRecord *Deque;
struct DequeRecord {
    PtrToNode Front, Rear;
};
Deque CreateDeque();
int Push( ElementType X, Deque D );
ElementType Pop( Deque D );
int Inject( ElementType X, Deque D );
ElementType Eject( Deque D );

Operation GetOp();          /* details omitted */
void PrintDeque( Deque D ); /* details omitted */

int main()
{
    ElementType X;
    Deque D;
    int done = 0;

    D = CreateDeque();
    while (!done) {
        switch(GetOp()) {
        case push: 
            scanf("%d", &X);
            if (!Push(X, D)) printf("Memory is Full!\n");
            break;
        case pop:
            X = Pop(D);
            if ( X==ERROR ) printf("Deque is Empty!\n");
            break;
        case inject: 
            scanf("%d", &X);
            if (!Inject(X, D)) printf("Memory is Full!\n");
            break;
        case eject:
            X = Eject(D);
            if ( X==ERROR ) printf("Deque is Empty!\n");
            break;
        case end:
            PrintDeque(D);
            done = 1;
            break;
        }
    }
    return 0;
}

/* Your function will be put here */

Sample Input:

Pop
Inject 1
Pop
Eject
Push 1
Push 2
Eject
Inject 3
End

Sample Output:

Deque is Empty!
Deque is Empty!
Inside Deque: 2 3
这个题只要能明白双向队列的功能原理实现就好说了
//模型:front不动,每次插入的时候是在front和它下一个节点之间插入,而rear每次都是移动的,每次都是插到后面,并且rear每次都要
Deque CreateDeque(){//移动指向最后一个上。
    Deque p;
    p = (Deque)malloc(sizeof(struct DequeRecord));//创建头尾指针
    p->Front = (PtrToNode)malloc(sizeof(struct Node));//在头上先创建一个虚的节点
    p->Front->Last = NULL;//前一个为null说明队列左边没东西
    p->Rear = p->Front;//左右相同指向同一个
    p->Rear->Next = NULL;//后一个为null说明右边没有东西
    return p;
}
int Push( ElementType X, Deque D ){//每次在front和下一个节点之间插入
    struct Node* temp;
    temp = (struct Node*)malloc(sizeof(struct Node));
    if(!temp)return 0;//内存满了,申请失败
    temp->Element = X;//赋值
    if(D->Front==D->Rear){//如果是个空双端队列
        D->Front->Next = temp;//插入第一个点
        temp->Last = D->Front;//回指向front
        D->Rear = temp;//rear指针移动指向第一个新插入的,代表从左边进入的第一个最靠近右边
        temp->Next = NULL;//第一次在左边进入的第一个点下一个将不再指向任何东西,因为每次
        return 1;         //都在前一个插入,第一个点会越来越远
    }
    //一般情况
    temp->Next = D->Front->Next;//新节点下一个指向原来front指向的下一个
    temp->Last = D->Front;//新节点的前一个指向fronr
    D->Front->Next->Last = temp;//front原来所值元素的前一个指向新的节点
    D->Front->Next = temp;//front的下一个指向新节点
    return 1;
}
ElementType Pop( Deque D ){
    if(D->Front==D->Rear)
        return ERROR;//如果空队列返回错误
    int temp = D->Front->Next->Element;//保存pop出的值
    struct Node* t = D->Front->Next;//保存它是为了最后把它内存释放掉
    if(D->Front->Next==D->Rear){//队列中只有一个元素的时候
        D->Rear = D->Front;//删除后rear前移使得rear和front相等
        D->Rear->Next = NULL;//虚节点指向空
        free(t);
        return temp;
    }
    //一般情况
    D->Front->Next->Next->Last = D->Front;//我们要删除front的前一个所以删除之后front前一个的前一个的Last应该指回Front
    D->Front->Next = D->Front->Next->Next;//同理front下一个应该是原来没删前下一个的下一个
    free(t);
    return temp;
}
int Inject( ElementType X, Deque D ){//从右边插入就直接插到后面,然后rear后移
    struct Node* temp = (struct Node*)malloc(sizeof(struct Node));
    if(!temp)return 0;
    temp->Element = X;
    if(D->Front==D->Rear){//空双端队列
        D->Front->Next = temp;
        temp->Last = D->Front;
        D->Rear = temp;
        return 1;//和push的一样
    }
    //一般情况
    D->Rear->Next = temp;//rear下一个等于新节点
    temp->Last = D->Rear;//新节点前一个等于现在rear指的点
    temp->Next = NULL;//temp的下一个指向空
    D->Rear = temp;//rear右移到当前点
    return 1;
}
ElementType Eject( Deque D ){
    if(D->Front==D->Rear){//空队列返回错误
        return ERROR;
    }
    int temp = D->Rear->Element;//保存值
    struct Node* t = D->Rear;
    D->Rear = D->Rear->Last;//删掉节点rear指回前一个节点
    D->Rear->Next = NULL;//现在节点为最后一个,所以指向空
    free(t);
    return temp;

}


 
import matplotlib.pyplot as plt import numpy as np from typing import List, Tuple class Point: """二维点类""" def __init__(self, x: float, y: float): self.x = x self.y = y def __sub__(self, other): return Point(self.x - other.x, self.y - other.y) def __eq__(self, other): return abs(self.x - other.x) < 1e-10 and abs(self.y - other.y) < 1e-10 def __repr__(self): return f"({self.x:.2f}, {self.y:.2f})" def cross_product(o: Point, a: Point, b: Point) -> float: """计算向量oa和ob的叉积[6,7](@ref)""" return (a.x - o.x) * (b.y - o.y) - (b.x - o.x) * (a.y - o.y) def orientation(o: Point, a: Point, b: Point) -> int: """判断点b相对于向量oa的位置[6,7](@ref)""" val = cross_product(o, a, b) if abs(val) < 1e-10: return 0 # 共线 return 1 if val > 0 else -1 # 左侧为1,右侧为-1 class MelkmanConvexHull: """Melkman凸包算法实现[6,7](@ref)""" def __init__(self): self.hull_history = [] # 记录凸包计算过程 self.operation_log = [] # 记录操作日志 def melkman_algorithm(self, polygon: List[Point]) -> List[Point]: """ Melkman算法实现 - O(n)时间复杂度[6,7](@ref) 参数: polygon: 简单多边形的有序顶点列表 返回: 凸包顶点(逆时针顺序) """ n = len(polygon) if n < 3: return polygon.copy() # 初始化双端队列[6](@ref) deque = [] self.operation_log.append("算法开始:初始化双端队列") # 处理前三个点,确保逆时针顺序[7](@ref) o = orientation(polygon[0], polygon[1], polygon[2]) if o == 0: # 共线情况,寻找不共线的三个点 for i in range(2, n): o = orientation(polygon[0], polygon[1], polygon[i]) if o != 0: break if o >= 0: # 顺时针或共线 deque = [polygon[2], polygon[0], polygon[1], polygon[2]] self.operation_log.append("前三点为顺时针顺序,调整队列顺序") else: # 逆时针 deque = [polygon[2], polygon[1], polygon[0], polygon[2]] self.operation_log.append("前三点为逆时针顺序,保持队列顺序") self.hull_history.append(deque.copy()) self.operation_log.append(f"初始队列: {deque}") # 处理剩余顶点[6](@ref) for i in range(3, n): current_point = polygon[i] self.operation_log.append(f"\n处理顶点 {i}: {current_point}") # 检查点是否在当前凸包内部[7](@ref) is_inside = (orientation(deque[-2], deque[-1], current_point) > 0 and orientation(deque[1], deque[0], current_point) > 0) if is_inside: self.operation_log.append(f"顶点 {current_point} 在凸包内部,跳过") continue # 从队列底部移除非凸顶点[6](@ref) while len(deque) > 1 and orientation(deque[0], deque[1], current_point) <= 0: removed = deque.pop(0) self.operation_log.append(f"从底部移除顶点: {removed}") # 在队列底部添加新顶点 deque.insert(0, current_point) self.operation_log.append(f"在底部添加顶点: {current_point}") # 从队列顶部移除非凸顶点[6](@ref) while len(deque) > 1 and orientation(deque[-2], deque[-1], current_point) <= 0: removed = deque.pop() self.operation_log.append(f"从顶部移除顶点: {removed}") # 在队列顶部添加新顶点 deque.append(current_point) self.operation_log.append(f"在顶部添加顶点: {current_point}") self.hull_history.append(deque.copy()) self.operation_log.append(f"当前队列状态: {deque}") # 清理结果,移除重复顶点 hull = [] for point in deque: if point not in hull: hull.append(point) # 确保逆时针顺序 if len(hull) >= 3: # 计算面积判断方向 area = 0 for i in range(len(hull)): j = (i + 1) % len(hull) area += hull[i].x * hull[j].y - hull[j].x * hull[i].y if area < 0: # 顺时针,需要反转 hull.reverse() self.operation_log.append("反转凸包为逆时针顺序") self.operation_log.append(f"算法完成,凸包顶点数: {len(hull)}") return hull def plot_polygon_and_hull(polygon: List[Point], hull: List[Point], title: str, show_vertices: bool = True, save_path: str = None): """绘制多边形和凸包""" plt.figure(figsize=(12, 10)) # 绘制原始多边形 poly_x = [p.x for p in polygon] + [polygon[0].x] poly_y = [p.y for p in polygon] + [polygon[0].y] plt.plot(poly_x, poly_y, 'b-', alpha=0.7, linewidth=2, label='原始多边形') if show_vertices: plt.scatter([p.x for p in polygon], [p.y for p in polygon], c='blue', s=50, alpha=0.6, marker='o') # 绘制凸包 hull_x = [p.x for p in hull] + [hull[0].x] hull_y = [p.y for p in hull] + [hull[0].y] plt.plot(hull_x, hull_y, 'r-', linewidth=3, label='凸包') plt.scatter([p.x for p in hull], [p.y for p in hull], c='red', s=100, marker='s', zorder=5) # 标记顶点编号 for i, point in enumerate(polygon): plt.annotate(str(i), (point.x, point.y), xytext=(5, 5), textcoords='offset points', fontsize=10, color='blue') plt.title(title, fontsize=14, fontweight='bold') plt.xlabel('X坐标', fontsize=12) plt.ylabel('Y坐标', fontsize=12) plt.legend(fontsize=11) plt.grid(True, alpha=0.3) plt.axis('equal') if save_path: plt.savefig(save_path, dpi=300, bbox_inches='tight') print(f"图像已保存至: {save_path}") plt.show() def test_melkman_algorithm(): """测试Melkman算法""" melkman = MelkmanConvexHull() print("=" * 60) print("Melkman凸包算法测试") print("=" * 60) # 测试用例1:凸多边形(正六边形) print("\n1. 测试凸多边形(正六边形)") angles = np.linspace(0, 2*np.pi, 7)[:-1] hexagon = [Point(np.cos(a), np.sin(a)) for a in angles] hull1 = melkman.melkman_algorithm(hexagon) print(f"原始顶点数: {len(hexagon)}") print(f"凸包顶点数: {len(hull1)}") print(f"凸包顶点: {hull1}") plot_polygon_and_hull(hexagon, hull1, "测试1: 凸多边形(正六边形)") # 测试用例2:非凸多边形(星形) print("\n2. 测试非凸多边形(星形)") star_points = [ Point(0, 3), Point(1, 1), Point(3, 1), Point(1.5, 0), Point(2, -2), Point(0, -1), Point(-2, -2), Point(-1.5, 0), Point(-3, 1), Point(-1, 1) ] hull2 = melkman.melkman_algorithm(star_points) print(f"原始顶点数: {len(star_points)}") print(f"凸包顶点数: {len(hull2)}") print(f"凸包顶点: {hull2}") plot_polygon_and_hull(star_points, hull2, "测试2: 非凸多边形(星形)") # 测试用例3:随机简单多边形 print("\n3. 测试随机简单多边形") np.random.seed(42) n_points = 20 random_points = [Point(x, y) for x, y in np.random.rand(n_points, 2)] # 按极角排序创建简单多边形 center = Point(0.5, 0.5) random_points.sort(key=lambda p: np.arctan2(p.y - center.y, p.x - center.x)) hull3 = melkman.melkman_algorithm(random_points) print(f"原始顶点数: {len(random_points)}") print(f"凸包顶点数: {len(hull3)}") print(f"凸包顶点: {hull3}") plot_polygon_and_hull(random_points, hull3, "测试3: 随机简单多边形") # 打印算法操作日志 print("\n4. 算法操作日志") print("-" * 40) for log in melkman.operation_log[:10]: # 只显示前10条日志 print(log) if len(melkman.operation_log) > 10: print(f"... 还有 {len(melkman.operation_log) - 10} 条日志") return hull1, hull2, hull3 def complexity_analysis(): """算法复杂度分析[6,7](@ref)""" print("\n" + "=" * 60) print("Melkman算法复杂度分析") print("=" * 60) analysis = """ 时间复杂度分析: - 每个顶点最多被加入双端队列两次(底部和顶部) - 每个顶点最多被移除两次(从底部和顶部) - 总操作次数为 O(4n) = O(n) - 算法具有线性时间复杂度 空间复杂度分析: - 双端队列最多存储 2n 个顶点引用 - 空间复杂度为 O(n) 算法优势: 1. 适用于任意简单多边形(不自交) 2. 不需要预处理排序步骤 3. 在线算法:可以逐个处理顶点 4. 实现简洁,效率高 关键点: - 利用双端队列同时维护凸包的左右链 - 通过叉积判断点的相对位置 - 利用简单多边形的性质进行局部调整 """ print(analysis) if __name__ == "__main__": # 运行测试用例 hull1, hull2, hull3 = test_melkman_algorithm() # 显示复杂度分析 complexity_analysis() # 验证算法正确性 print("\n" + "=" * 60) print("算法验证") print("=" * 60) # 检查凸包性质 def is_convex(hull: List[Point]) -> bool: """检查多边形是否为凸的""" n = len(hull) if n < 3: return False for i in range(n): a, b, c = hull[i], hull[(i+1)%n], hull[(i+2)%n] if orientation(a, b, c) < 0: return False return True test_cases = [hull1, hull2, hull3] for i, hull in enumerate(test_cases, 1): convex = is_convex(hull) print(f"测试用例 {i}: 凸包验证 {'通过' if convex else '失败'}") print(f" 顶点数: {len(hull)}, 凸性: {convex}") 用这个里面的例子 Melkman’s Algorithm O(n)
10-23
import cv2 import numpy as np import random from collections import deque import mediapipe as mp import json import os from datetime import datetime from PIL import Image, ImageDraw, ImageFont import platform # ------------------------------ # 检测系统并选择合适的中文字体路径 # ------------------------------ def get_chinese_font(): system = platform.system() font_path = None if system == "Windows": font_path = "C:/Windows/Fonts/simhei.ttf" # 黑体 elif system == "Darwin": # macOS font_path = "/System/Library/Fonts/PingFang.ttc" elif system == "Linux": for path in [ "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc" ]: if os.path.exists(path): font_path = path break try: return ImageFont.truetype(font_path, 32) if font_path else ImageFont.load_default() except Exception as e: print(f"⚠️ 加载中文字体失败: {e}") return ImageFont.load_default() CHINESE_FONT = get_chinese_font() # ------------------------------ # 在 OpenCV 图像上绘制中文(使用 PIL) # ------------------------------ def put_chinese_text(image, text, position, color=(255, 255, 255), font_size=30): """使用 Pillow 绘制中文文本""" try: if hasattr(CHINESE_FONT, 'path'): font = ImageFont.truetype(CHINESE_FONT.path, font_size) else: font = ImageFont.load_default() print("⚠️ 使用默认字体渲染中文") except: font = ImageFont.load_default() pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) draw = ImageDraw.Draw(pil_image) draw.text(position, text, fill=tuple(color), font=font) return cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR) # ------------------------------ # 排行榜管理 # ------------------------------ RANKING_FILE = "snake_ranking.json" MAX_RECORDS = 100 def load_ranking(): if not os.path.exists(RANKING_FILE): return [] try: with open(RANKING_FILE, 'r', encoding='utf-8') as f: data = json.load(f) seen = set() unique_data = [] for item in data: key = (item.get('score', 0), item.get('timestamp', '')) if key not in seen: seen.add(key) unique_data.append(item) return sorted(unique_data, key=lambda x: x['score'], reverse=True) except Exception as e: print("加载排行榜失败:", e) return [] def save_best_score(score): ranking = load_ranking() current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") new_record = {"score": score, "timestamp": current_time} ranking.append(new_record) seen = set() unique_ranking = [] for r in ranking: key = (r['score'], r['timestamp']) if key not in seen: seen.add(key) unique_ranking.append(r) unique_ranking = sorted(unique_ranking, key=lambda x: x['score'], reverse=True)[:MAX_RECORDS] try: with open(RANKING_FILE, 'w', encoding='utf-8') as f: json.dump(unique_ranking, f, indent=2, ensure_ascii=False) except Exception as e: print("保存排行榜失败:", e) def clear_ranking(): frame = np.zeros((720, 1280, 3), dtype=np.uint8) for y in range(720): color_val = 20 + y // 20 frame[y, :] = [color_val, color_val // 2, 60] h, w = frame.shape[:2] center_x = w // 2 frame = put_chinese_text(frame, "⚠️ 清空所有记录?", (center_x - 240, h // 2 - 60), color=(0, 0, 255), font_size=48) frame = put_chinese_text(frame, "确定要清空吗?(Y=是, N=否)", (center_x - 260, h // 2), color=(255, 255, 255), font_size=36) frame = put_chinese_text(frame, "此操作不可撤销!", (center_x - 180, h // 2 + 50), color=(150, 150, 150), font_size=32) cv2.imshow("Hand-Controlled Snake Game", frame) while True: key = cv2.waitKey(1) & 0xFF if key in (ord('y'), ord('Y')): try: with open(RANKING_FILE, 'w') as f: f.write("[]") return True except Exception as e: print("清空失败:", e) return False elif key in (ord('n'), ord('N'), 27): # ESC return False # ------------------------------ # 主菜单界面 # ------------------------------ def show_ranking_menu(): ranking = load_ranking() frame = np.zeros((720, 1280, 3), dtype=np.uint8) for y in range(720): color_val = 20 + y // 20 frame[y, :] = [color_val, color_val // 2, 60] h, w = frame.shape[:2] center_x = w // 2 frame = put_chinese_text(frame, "🏆 贪吃蛇游戏排行榜 🏆", (center_x - 280, 40), color=(255, 255, 100), font_size=50) frame = put_chinese_text(frame, "名次 得分 时间", (center_x - 240, 90), color=(200, 255, 255), font_size=30) start_y = 140 for i, item in enumerate(ranking[:10]): text = f"{i + 1:2d} {item['score']:4d} {item['timestamp']}" color = (0, 255, 255) if i == 0 else (200, 255, 200) frame = put_chinese_text(frame, text, (center_x - 240, start_y + i * 32), color=color, font_size=28) if len(ranking) == 0: frame = put_chinese_text(frame, "暂无记录...", (center_x - 100, start_y), color=(150, 150, 150), font_size=30) button_w = 200 btn_y1, btn_y2 = h - 140, h - 80 # 开始按钮 start_x1, start_x2 = center_x - button_w - 50, center_x - 50 cv2.rectangle(frame, (start_x1, btn_y1), (start_x2, btn_y2), (0, 180, 0), -1) cv2.rectangle(frame, (start_x1, btn_y1), (start_x2, btn_y2), (0, 255, 0), 3) frame = put_chinese_text(frame, "开始游戏", (center_x - button_w // 2 - 50, h - 100), color=(255, 255, 255), font_size=40) # 退出按钮 quit_x1, quit_x2 = center_x + 50, center_x + button_w + 50 cv2.rectangle(frame, (quit_x1, btn_y1), (quit_x2, btn_y2), (180, 0, 0), -1) cv2.rectangle(frame, (quit_x1, btn_y1), (quit_x2, btn_y2), (255, 0, 0), 3) frame = put_chinese_text(frame, "退出游戏", (center_x + button_w // 2 - 40, h - 100), color=(255, 255, 255), font_size=40) # 清空按钮 clear_x1, clear_x2 = center_x - 100, center_x + 100 cv2.rectangle(frame, (clear_x1, h - 70), (clear_x2, h - 10), (100, 100, 100), -1) cv2.rectangle(frame, (clear_x1, h - 70), (clear_x2, h - 10), (200, 200, 200), 2) frame = put_chinese_text(frame, "清空记录", (center_x - 45, h - 30), color=(255, 255, 255), font_size=32) frame = put_chinese_text(frame, "按'S'开始|'Q'退出|'C'清空", (center_x - 240, h - 160), color=(200, 200, 255), font_size=24) cv2.imshow("Hand-Controlled Snake Game", frame) while True: key = cv2.waitKey(1) & 0xFF if key in (ord('s'), ord('S')): return "start" elif key in (ord('q'), ord('Q'), 27): return "quit" elif key in (ord('c'), ord('C')): result = clear_ranking() return "refresh" # ------------------------------ # 初始化 MediaPipe Hands # ------------------------------ mp_hands = mp.solutions.hands mp_draw = mp.solutions.drawing_utils hands = mp_hands.Hands( static_image_mode=False, max_num_hands=1, min_detection_confidence=0.7, min_tracking_confidence=0.5 ) # ------------------------------ # 游戏参数设置 # ------------------------------ width, height = 1280, 720 snake_speed = 8 snake_length = 3 score = 0 game_over = False walls = [] cap = cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height) snake = deque([(width // 2, height // 2)]) direction = np.array([1.0, 0.0]) target_pos = None food_radius = 15 food_color = (0, 255, 0) food = [random.randint(50, width - 50), random.randint(50, height - 50)] # ------------------------------ # 工具函数 # ------------------------------ def is_point_in_wall(x, y, walls): return any(wx <= x <= wx + ww and wy <= y <= wy + wh for (wx, wy, ww, wh) in walls) def check_wall_collision(head, walls): hx, hy = head return any(wx <= hx <= wx + ww and wy <= hy <= wy + wh for (wx, wy, ww, wh) in walls) def generate_food(snake_body, walls): max_attempts = 100 min_dist = 30 for _ in range(max_attempts): x = random.randint(50, width - 50) y = random.randint(50, height - 50) pos = np.array([x, y]) if is_point_in_wall(x, y, walls): continue if any(np.linalg.norm(pos - np.array(seg)) < min_dist for seg in snake_body): continue return [x, y] return [width - 60, height - 60] # BFS 路径可达性检查(略去以保持简洁,实际项目建议保留) # 此处简化为仅距离判断(用于快速测试) def is_path_available(head_pos, food_pos, walls): # 简化版:只要不直接被墙堵死就算通路 return not is_point_in_wall(food_pos[0], food_pos[1], walls) def add_wall_safely(walls_list, snake_body, food_pos): if len(walls_list) >= 5: return walls_list for _ in range(30): ww = random.randint(40, 150) wh = random.randint(40, 150) wx = random.randint(50, width - ww - 50) wy = random.randint(50, height - wh - 50) new_wall = (wx, wy, ww, wh) temp_walls = walls_list + [new_wall] if not is_point_in_wall(snake_body[0][0], snake_body[0][1], temp_walls) and \ not is_point_in_wall(food_pos[0], food_pos[1], temp_walls): walls_list.append(new_wall) break return walls_list # ------------------------------ # 主游戏循环 # ------------------------------ while True: action = show_ranking_menu() if action == "quit": break elif action == "refresh": continue elif action == "start": # 重置游戏 snake.clear() snake.append((width // 2, height // 2)) direction = np.array([1.0, 0.0]) snake_length = 3 score = 0 game_over = False walls = [] target_pos = None food = generate_food(snake, walls) while cap.isOpened(): ret, frame = cap.read() if not ret: break frame = cv2.flip(frame, 1) rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 👇 安全调用 MediaPipe 并检查结果 results = hands.process(rgb_frame) new_target = None # ✅ 关键修复:先检查是否检测到手 if not game_over and results is not None: # ✅ 使用 hasattr 和 None 判断避免 IDE 警告 if hasattr(results, 'multi_hand_landmarks') and results.multi_hand_landmarks: for hand_landmarks in results.multi_hand_landmarks: mp_draw.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS) idx_tip = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP] x, y = int(idx_tip.x * width), int(idx_tip.y * height) # 更新目标点(平滑滤波) if target_pos is None: target_pos = (x, y) else: alpha = 0.3 target_pos = ( int(alpha * x + (1 - alpha) * target_pos[0]), int(alpha * y + (1 - alpha) * target_pos[1]) ) new_target = target_pos # 显示目标点(紫色圆圈) cv2.circle(frame, target_pos, 12, (255, 0, 255), -1) # 移动逻辑 if not game_over: head = np.array(snake[0]) if new_target is not None: to_target = np.array(new_target) - head dist = np.linalg.norm(to_target) if dist > 30: desired_dir = to_target / (dist + 1e-8) curr_dir_norm = direction / (np.linalg.norm(direction) + 1e-8) dot = np.clip(np.dot(curr_dir_norm, desired_dir), -1.0, 1.0) angle = np.arccos(dot) if angle > 0.25: # 最大转向弧度 w1 = np.sin(angle - 0.25) w2 = np.sin(0.25) sin_a = np.sin(angle) blended = (w1 * curr_dir_norm + w2 * desired_dir) / (sin_a + 1e-8) direction = blended / (np.linalg.norm(blended) + 1e-8) else: direction = desired_dir new_head = head + direction * snake_speed new_head = np.round(new_head).astype(int) snake.appendleft(tuple(new_head)) # 碰撞检测 if (check_wall_collision(new_head, walls) or new_head[0] <= 0 or new_head[0] >= width or new_head[1] <= 0 or new_head[1] >= height): game_over = True for i in range(4, len(snake)): seg = np.array(snake[i]) if np.linalg.norm(new_head - seg) < 15: game_over = True break # 吃食物 if np.linalg.norm(new_head - np.array(food)) < (food_radius + 10): old_score = score score += 1 food = generate_food(snake, walls) snake_length += 2 if score // 5 > old_score // 5: walls = add_wall_safely(walls, snake, food) while len(snake) > snake_length: snake.pop() # 绘图部分 for wall in walls: wx, wy, ww, wh = wall cv2.rectangle(frame, (wx, wy), (wx + ww, wy + wh), (255, 255, 0), -1) cv2.rectangle(frame, (wx, wy), (wx + ww, wy + wh), (0, 0, 0), 3) cv2.circle(frame, tuple(food), food_radius, food_color, -1) for i in range(1, len(snake)): alpha = i / len(snake) color = (0, int(255 * (1 - alpha)), int(100 * (1 - alpha))) cv2.line(frame, snake[i - 1], snake[i], color, 8) cv2.circle(frame, snake[0], 10, (255, 255, 255), -1) cv2.circle(frame, snake[0], 8, (0, 150, 0), -1) frame = put_chinese_text(frame, f"得分: {score}", (20, 15), color=(255, 255, 255), font_size=50) if game_over: overlay = frame.copy() cv2.rectangle(overlay, (width // 4, height // 4), (3 * width // 4, 3 * height // 4), (0, 0, 0), -1) cv2.addWeighted(overlay, 0.7, frame, 0.3, 0, frame) frame = put_chinese_text(frame, "游戏结束", (width // 2 - 120, height // 2 - 60), color=(0, 0, 255), font_size=60) frame = put_chinese_text(frame, "按任意键返回主菜单", (width // 2 - 160, height // 2), color=(255, 255, 255), font_size=36) frame = put_chinese_text(frame, f"最终得分: {score}", (width // 2 - 120, height // 2 + 50), color=(255, 255, 0), font_size=32) cv2.imshow("Hand-Controlled Snake Game", frame) cv2.waitKey(0) break cv2.imshow("Hand-Controlled Snake Game", frame) if cv2.waitKey(1) == 27: # ESC break # ------------------------------ # 释放资源 # ------------------------------ cap.release() cv2.destroyAllWindows() hands.close()
最新发布
11-08
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值