基于EasyX图形库的汉诺塔可视化模型

模型示意图:

在这里插入图片描述
在这里插入图片描述

需求分析

  • 1、实现汉诺塔每层移动的动画效果
  • 2、实现通过用户指定层数来进行相应层数的移动效果

总体设计

  • 1、模型基于EasyX图形库实现可视化效果
  • 2、编程语言采用C语言
  • 3、采用这种数据结构的思想来实现层的迁移(例如:将A塔的最顶层移动到B塔的最顶层)

详细设计

1、可视化界面设计:

  • 大小
    • 宽度:500px
    • 高度:600px
  • 代码:
#include <graphics.h>	// 导入EasyX图形库头文件
#include <stdio.h>

// main()
int main() {
	initgraph(500, 600);	// 初始化一个500*600的窗口	
	cleardevice();			// 刷新窗口
	
	closegraph();			// 关闭窗口
	return 0;
}

2、变量设计

  • 这里我采用了面象对象程序的设计思想,将塔和层都是抽象为结构体,这样设计的好处不仅可以对程序中诸多变量的管理,也能更好的采用的思想来设计程序。
  • 层的设计:
    • 位置:
      • 横坐标:x
      • 纵坐标:y
    • 大小:
      • 宽度:width
      • 高度:height
    • 颜色
      • RGB:color_rgb[3];
    • 指向下一个结点的指针:
      • 指针:next
    • 代码:
typedef struct PLATE {
	int x;
	int y;
	int width;
	int height;
	int color_rgb[3];
	struct PLATE* next;
}*plate;
  • 塔的设计:
    • 位置:
      • 横坐标:x
      • 纵坐标:y
    • 栈顶指针:top(因为采用栈的思想所以我将每座塔设计为栈的结构)
    • 代码:
// 塔
typedef struct PILLAR {
	int x;
	int y;
	struct PLATE* top;
}*pillar;
  • 计步器:
    • step
    • 代码:
//步子统计
int step = 0;

3、函数设计

  • main函数的设计
    • 窗口的创建,三座塔的初始化,以及陈序的主要流程
    • 代码:
// main()
int main() {
	initgraph(500, 600);	//初始化一个500*600的窗口
	BeginBatchDraw();		
	cleardevice();			// 刷新屏幕
	srand((unsigned)time(NULL));
	//定义三座塔
	pillar A = newPillar(100, 400);
	pillar B = newPillar(250, 400);
	pillar C = newPillar(400, 400);
	//定义层数
	int n = 6;
	batchAdd(A, n);
	draw(A, B, C, n);
	char ch = _getch();
	while (ch == ' ') {
		move(n, A, B, C);	// 移动层
		ch = _getch();
		A->top = NULL;
		B->top = NULL;
		C->top = NULL;
		step = 0;
		batchAdd(A, n);		// 将层全部按小层在大层上面的顺序放入A塔
	}
	ch = _getch();
	EndBatchDraw();
	closegraph();
	return 0;
}
  • 层的初始化:
    • 采用面向对象设计思想,将函数设计为层对象的构造器
    • 代码:
//初始化层
plate newPlate(int x, int y, int width, int height, int R, int G, int B) {
	plate p = (plate)malloc(sizeof(struct PLATE));
	if (p == NULL) {
		printf("创建层失败!\n");
		exit(NULL);
	}
	p->x = x;
	p->y = y;
	p->width = width;
	p->height = height;
	p->color_rgb[0] = R;
	p->color_rgb[1] = G;
	p->color_rgb[2] = B;
	p->next = NULL;
	return p;
}
  • 塔的初始化
    • 设计思想同层
    • 代码:
//初始化塔
pillar newPillar(int x, int y) {
	pillar p = (pillar)malloc(sizeof(pillar));
	if (p == NULL) {
		printf("创建塔失败!\n");
		exit(NULL);
		return NULL;
	}
	p->x = x;
	p->y = y;
	p->top = NULL;
	return p;
}
  • 入栈操作
    • 传入塔和层,将层放入塔顶(栈顶)
    • 代码
//入栈
void add(pillar &pil, plate pla) {
	if (pla == NULL) {
		printf("盘子不存在!\n");
		return;
	}
	pla->next = pil->top;	//将入栈元素的next指针指向栈顶元素
	pil->top = pla;		// 令入栈元素成为栈顶元素
}
  • 出栈操作
    • 传入塔,移除塔顶(栈顶元素)
    • 代码:
//出栈
plate pop(pillar &pil) {
	if (pil->top == NULL)
	{
		printf("该柱子为空柱!\n");
		exit(NULL);
		return NULL;
	}
	plate p = pil->top;		// 记录栈顶元素(要出栈的元素)位置
	pil->top = pil->top->next;	 // 将栈顶指针移动后一个元素
	return p;	// 将出栈元素返回
}
  • 批量初始化层并放入指定塔内
    • 传入塔及层数,依次从大到小初始化层并放入塔中
    • 代码:
// 批量生成层并将它们放入塔中
void batchAdd(pillar &pil, int n) {
	for (int i = 0; i < n; i++) {
		add(	// 将层放入塔中
			pil, // 要放入的塔
			newPlate(	// 通过层数,安从大到小的顺序以及塔的位置计算出每层的大小和位置来初始化每层
				pil->x - (n - i) * 20 / 2,
				pil->y - (i + 1) * 20, 
				(n - i) * 20, 
				20, 
                // 随机生成每层的颜色
				rand() % 255, 	
				rand() % 255, 
				rand() % 255
			)
		);
	}
}
  • 绘制层
    • 传入塔,以便绘制其中的每层
    • 代码:
// 绘制层
void drawPlate(pillar &pil) {
	if (pil->top == NULL) {
		exit(NULL);
	}
	plate p = pil->top;
	while (p != NULL) {	// 遍历栈
		setfillcolor(RGB(p->color_rgb[0], p->color_rgb[1], p->color_rgb[2]));
		fillrectangle(p->x, p->y, p->x + p->width, p->y + p->height);
		p = p->next;
	}
}
  • 整体绘制
    • 绘制包括层在内的塔、层数和步数的绘制
    • 代码:
// 绘制
void draw(pillar &A, pillar &B, pillar &C, int p_n) {
	int n = 4;
	wchar_t str[2];
	//画ABC塔
	line(A->x, A->y, A->x, A->y - n * 50);
	line(A->x - n * 35 / 2, A->y, A->x + n * 35 / 2, A->y);
	_stprintf(str, _T("A"));
	outtextxy(90, 410, str);

	line(B->x, B->y, B->x, B->y - n * 50);
	line(B->x - n * 35 / 2, B->y, B->x + n * 35 / 2, B->y);
	_stprintf(str, _T("B"));
	outtextxy(240, 410, str);

	line(C->x, C->y, C->x, C->y - n * 50);
	line(C->x - n * 35 / 2, C->y, C->x + n * 35 / 2, C->y);
	_stprintf(str, _T("C"));
	outtextxy(390,410, str);

	//画盘子
	if (A->top != NULL) {
		drawPlate(A);
	}
	if (B->top != NULL) {
		drawPlate(B);
	}
	if (C->top != NULL) {
		drawPlate(C);
	}

	// 数据可视化
	wchar_t str_n[20];
	wchar_t str_step[20];
	_stprintf(str_n, _T("层数:%d"), p_n);
	_stprintf(str_step, _T("步数:%d"), step);
	settextcolor(RGB(255, 255, 255));
	settextstyle(15, 15, _T("宋体"));
	outtextxy(10, 10, str_n);
	outtextxy(10, 30, str_step);
}
  • 移动动画
    • 将三座塔以及层数传入,实现将某个塔的最顶层移动另一个塔的最顶层
    • 代码:
// 移动动画
void MoveAnimation(pillar &A, pillar &B, pillar &C, int p_n) {
	int n = 4;
	int temp;
	if (B->top != NULL) {
		temp = B->top->y - B->top->height;
	}
	else {
		temp = B->y - A->top->height;
	}
    // 循环判断移出塔的塔顶层的位置直到该层到达移入塔的塔顶为止
	while (A->top->x != B->x - A->top->width / 2 || A->top->y != temp) {
        // 判断是否移出
		if (A->top->y >= A->y - n * 50 - 10 && A->top->x == A->x - A->top->width / 2) {
			A->top->y--;
		}
        // 判断是否移到移入塔的上方
		else if (A->top->x != B->x - A->top->width / 2) {
            // 判读是左移还是右移
			if (A->top->x < B->x - A->top->width / 2) {
				A->top->x++;
			}
			else {
				A->top->x--;
			}
		}
        // 判断是否到达移入塔的塔顶位置
		else if (A->top->y < temp) {
			A->top->y++;
		}
		draw(A, B, C, p_n);
		FlushBatchDraw();
		//Sleep(10);
		cleardevice();
	}
	add(B, pop(A)); //将A塔中最上面的一层移到B塔中
	step++;
}
  • 核心函数
    • 汉诺塔的递归算法
    • 代码
// 移动
void move(int n, pillar &A, pillar &B, pillar &C) {
	static int t = n;
	if (n == 0) return;
	move(n - 1, A, C, B);
	MoveAnimation(A, C, B, t);
	draw(A, B, C, t);
	move(n - 1, B, A, C);
}
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用EasyX图形库来实现链表的可视化。下面是一个简单的示例代码: ```c #include <graphics.h> #include <stdio.h> #include <stdlib.h> #define NODE_SIZE 40 // 定义节点的大小 #define LINE_WIDTH 5 // 定义连线的宽度 #define SCREEN_WIDTH 800 // 定义屏幕宽度 #define SCREEN_HEIGHT 600 // 定义屏幕高度 typedef struct node { int data; // 数据域 struct node* next; // 指针域 } Node; // 创建节点 Node* CreateNode(int data) { Node* node = (Node*)malloc(sizeof(Node)); node->data = data; node->next = NULL; return node; } // 插入节点 void InsertNode(Node* head, int pos, int data) { Node* pre = head; for (int i = 1; i < pos && pre != NULL; i++) { pre = pre->next; } if (pre == NULL) { return; } Node* node = CreateNode(data); node->next = pre->next; pre->next = node; } // 删除节点 void DeleteNode(Node* head, int pos) { Node* pre = head; for (int i = 1; i < pos && pre->next != NULL; i++) { pre = pre->next; } if (pre->next == NULL) { return; } Node* temp = pre->next; pre->next = temp->next; free(temp); } // 绘制链表 void DrawList(Node* head) { cleardevice(); // 清空画布 settextcolor(WHITE); setbkcolor(BLACK); int x = NODE_SIZE / 2, y = NODE_SIZE / 2; char str[10]; while (head != NULL) { sprintf(str, "%d", head->data); // 将整数转换为字符串 outtextxy(x - 10, y - 10, str); // 在画布上输出字符串 circle(x, y, NODE_SIZE / 2); // 绘制圆形 if (head->next != NULL) { line(x + NODE_SIZE / 2, y, x + NODE_SIZE * 3 / 2, y); // 绘制连线 } x += NODE_SIZE * 2; // 计算下一个节点的位置 if (x > SCREEN_WIDTH - NODE_SIZE / 2) { x = NODE_SIZE / 2; y += NODE_SIZE * 2; } head = head->next; } } int main() { Node* head = CreateNode(0); // 创建头节点 initgraph(SCREEN_WIDTH, SCREEN_HEIGHT); while (1) { DrawList(head); printf("请输入要进行的操作(1.插入 2.删除):"); int op; scanf("%d", &op); if (op == 1) { printf("请输入要插入的位置和数据,用空格隔开:"); int pos, data; scanf("%d %d", &pos, &data); InsertNode(head, pos, data); } else if (op == 2) { printf("请输入要删除的位置:"); int pos; scanf("%d", &pos); DeleteNode(head, pos); } } closegraph(); return 0; } ``` 这个程序使用了 `graphics.h` 和 `stdio.h` 头文件,其中 `graphics.h` 是 EasyX 图形库的头文件,`stdio.h` 是标准输入输出头文件。 程序定义了一个 `Node` 结构体,包含一个数据域和一个指针域,分别存储节点的数据和下一个节点的地址。定义了 `CreateNode` 函数用于创建节点,`InsertNode` 函数用于在指定位置插入节点,`DeleteNode` 函数用于删除指定位置的节点,`DrawList` 函数用于绘制链表。 在 `DrawList` 函数中,使用 `outtextxy` 函数在画布上输出每个节点的值,使用 `circle` 函数绘制圆形,使用 `line` 函数绘制连线。程序使用 `scanf` 函数从控制台读取用户输入的操作类型和参数,然后调用相应的函数进行操作,并在控制台输出操作结果。 程序使用了 `initgraph` 函数初始化 EasyX 图形库,使用 `closegraph` 函数关闭图形窗口。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值