# 杂项技术

背景

我们有的时候只是涉及到一些小文件,比如一些配置文件,一些小的图片等等,这些文件的内容不是很大,但是我们又不想直接把这些文件放到项目中,这个时候我们可以使用链接来直接存储这些小文件的内容。

而url支持使用 data 协议来使用base64格式直接存储小文件的内容,这样我们就可以直接使用链接来存储小文件的内容。

使用

我们可以使用如下的方式来存储小文件的内容:

1
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsSAAALEgHS3X78AAAA/0lEQVQ4jZXTsUoDQRDG8d8Q9" alt="Red dot" />

这里的 data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsSAAALEgHS3X78AAAA/0lEQVQ4jZXTsUoDQRDG8d8Q9 就是一个base64格式的图片内容,我们可以直接使用这个链接来展示图片。

然后我写了一个简单的脚本来生成base64格式的图片内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include <fstream>
#include <vector>
#include <string>
#include <iostream>

const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";

#include <chrono>

std::string base64_encode(const std::vector<unsigned char>& buffer) {
std::string encoded;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];

int totalBytes = buffer.size();
int progressInterval = totalBytes / 10; // Update progress every 10% of the total bytes

for (const auto& byte : buffer) {
char_array_3[i++] = byte;
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;

for (i = 0; (i < 4); i++)
encoded += base64_chars[char_array_4[i]];
i = 0;

// Update progress
if (encoded.size() % progressInterval == 0) {
int progress = (encoded.size() * 100) / totalBytes;
std::cout << "Progress: " << progress << "%" << std::endl;
}
}
}

if (i) {
for (j = i; j < 3; j++)
char_array_3[j] = '\0';

char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);

for (j = 0; (j < i + 1); j++)
encoded += base64_chars[char_array_4[j]];

while ((i++ < 3))
encoded += '=';
}

return encoded;
}

void fileToBase64(const std::string& path) {
std::ifstream file(path, std::ios::binary);
std::vector<unsigned char> buffer(std::istreambuf_iterator<char>(file), {});

std::string base64 = base64_encode(buffer);
std::string dataUrl = "data:application/x-rar-compressed;base64," + base64;
//输出到 “downloadLink_”+文件名.md文件

//1.获取不带路径的文件名
std::string::size_type iPos = path.find_last_of('\\') + 1;
std::string fileName = path.substr(iPos, path.length() - iPos);
std::cout << fileName << std::endl;

//2.获取不带后缀的文件名
std::string fileNameWithoutExtension = fileName.substr(0, fileName.rfind("."));
std::cout << fileNameWithoutExtension << std::endl;

//3.获取后缀名
std::string suffix_str = fileName.substr(fileName.find_last_of('.') + 1);
std::cout << suffix_str << std::endl;

std::string outputFileName = "downloadLink_" + fileNameWithoutExtension + ".md";
std::ofstream output(outputFileName);
//输出到md文件
//为md文件添加属性
//---
// title: downloadLink_下载文件名称
// date: 2024-05-02 17:36
// mathjax: false
// tags:
// - Download
// ---
output << "---" << std::endl;
output << "title: " << fileName << std::endl;
output << "date: 2024-03-01 17:36" << std::endl;
output << "mathjax: false" << std::endl;
output << "tags:" << std::endl;
output << " - Download" << std::endl;
output << "---" << std::endl;
//输出下载链接,文件名为原文件名
//以按钮的形式显示下载链接
output << "<a href=\"" << dataUrl << "\" download=\""<< fileName <<"\">点击下载</a>";
output.close();

//再在文件夹“DownloadLink”里添加一个只有属性但是不包含下载链接的md文件
std::string outputFileName2 = "downloadLink_" + fileNameWithoutExtension + ".md";
std::ofstream output2("DownloadLink\\" + outputFileName2);
//输出到md文件
//为md文件添加属性
//---
// title: downloadLink_下载文件名称
// date: 2024-05-02 17:36
// mathjax: false
// tags:
// - Download
// ---
output2 << "---" << std::endl;
output2 << "title: " << fileName << std::endl;
output2 << "mathjax: false" << std::endl;
output2 << "date: 2024-03-01 17:36" << std::endl;
output2 << "tags:" << std::endl;
output2 << " - Download" << std::endl;
output2 << "---" << std::endl;
output2.close();

std::cout << "Output file: " << outputFileName << std::endl;
}

//接收文件位置参数
int main(int argc, char* argv[]) {
//检查参数
if (argc < 2) {
std::cout << "Usage: " << argv[0] << " file1 [file2] [file3] ..." << std::endl;
return 1;
}
//支持多个文件
for (int i = 1; i < argc; i++){
//显示进度条
std::cout << "Processing " << argv[i] << "..." << std::endl;
fileToBase64(argv[i]);
}

//结束后暂停
std::cout << "Press any key to exit..." << std::endl;
std::cin.get();
return 0;
}

这个脚本可以将指定的文件转换为base64格式的内容,并且生成一个md文件,这个md文件包含了下载链接,我们可以直接使用这个md文件来展示下载链接。

下面就是一个使用这个脚本生成的md文件的例子:

1
2
3
4
5
6
7
8
---
title: test.rar
date: 2024-03-01 17:36
mathjax: false
tags:
- Download
---
<a href="data:application/x-rar-compressed;base64,UEsDBAoAAAAAAKx4pVgvcG0lQAAAAEAAAAAMAAAAtePO0rXjztIudHh05ZOI5ZOI5ZOI77yM5piv5LiN5piv5oy65aW9546p55qEDQoNCuS9v+eUqOmTvuaOpeS8oOi+k+WOi+e8qeWMhVBLAQI/AAoAAAAAAKx4pVgvcG0lQAAAAEAAAAAMACQAAAAAAAAAIAAAAAAAAAC1487StePO0i50eHQKACAAAAAAAAEAGACtFN2Yup7aAVB935i6ntoBiknN7yme2gFQSwUGAAAAAAEAAQBeAAAAagAAAAAA" download="test.zip">点击下载</a>

点击下载

这样我们就可以直接使用这个md文件来展示下载链接了。

# 杂项技术

背景

新版 QQnt 使用了基于 Node.js 的插件架构技术,这种技术可以让开发者通过编写插件来扩展 QQnt 的功能。Node.js 是一种 JavaScript 运行时环境,它可以让开发者使用 JavaScript 编写服务器端应用程序。

其使用了node.js来进行处理的话,就意味着我们可以使用插件,来对最终渲染的内容进行修改。实际上就和 【html网页可以使用js插件进行自定义】或者说【hexo可以使用node.js插件进行自定义一样】。

QQnt 的插件架构技术利用了 Node.js 的模块化特性,将不同的功能模块封装成独立的插件,并通过插件管理器来加载和管理这些插件。LiteLoaderQQnt就为我们提供了这样的一个插件管理器,下面将其简称为LLnt。

使用方式&注意事项

使用方式可以查看他们的github页面,有十分详细的教程,在这里就不过多赘述。
LiteLoaderQQNT

tips:设置里面所做出的更改既无法保存也无法生效

需要注意的是,软件必须获取qqnt安装位置的所有文件访问权限,不然的话,llnt设置里面所做出的更改既无法保存也无法生效。解决这个问题有两种办法:

方法一:调整安装位置

只需要将qq安装位置不要放在默认的program file下即可,因为访问里面的文件需要管理员权限。可以将qq安装到其他目录,比如C盘的根目录或者自定义的文件夹中。这样就可以获得访问权限,llnt设置里面的更改才能保存和生效。

方法二:将qqnt文件夹取消只读,以管理员身份启动qqnt

另一种解决方法是取消qqnt文件夹的只读属性,并以管理员身份启动qqnt。这样可以确保llnt设置里面的更改能够保存和生效。

要取消qqnt文件夹的只读属性,可以按照以下步骤操作:

  1. 在资源管理器中,找到qqnt文件夹的位置。
  2. 右键单击qqnt文件夹,并选择“属性”选项。
  3. 在属性对话框中,取消选中“只读”复选框。
  4. 单击“确定”按钮保存更改。

然后,以管理员身份启动qqnt,可以通过以下步骤完成:

  1. 找到qqnt的快捷方式或可执行文件。
  2. 右键单击快捷方式或可执行文件,并选择“以管理员身份运行”选项。

这样,llnt设置里面的更改就能够保存和生效了。

下面我保存了一些下载链接,虽然说按理来说可以直接上github链接的,但是总是感觉那个不太稳定(作者删库跑路),所以说这里相当于存一份~




阅读全文 »

# 泰坦陨落2

# 游戏体验日记

铁驭的平台跳跃

贴墙跑,二段跳。游戏将这两个操作性极大的元素加以组合,实现了铁驭独一无二的机动性……

泰坦和铁驭的相结合

游戏中最有魅力的不是开机甲,也不是铁驭的机动性,而是泰坦和铁驭的相结合。

泰坦会和铁驭一起协作,给予铁驭建议,接住铁驭,等等。尤其是从各个角度接住铁驭然后将其塞回驾驶舱,真的是太酷了……

优秀的故事叙事和世界观的展开

游戏的世界观很饱满,让玩家能够真正的感受到处于战场之上的感觉……

阅读全文 »

# 游戏体验日记

# 山海旅人

山海旅人:修改故事的因果……

简而言之吧,游戏的特色解密机制在于回溯事情的过去,然后收获“想法”来影响人。

比如,对于一个本来是要晚归的丈夫,收集家里“米饭很香”的想法,然后用这个想法来影响丈夫“晚点回去的决定”,就能够让其早点回去。

说实话,这和我的那个fairy tale 的 游戏设计机制的第一部分的想法相似,而最后的一小段则又是和我的想法中的最后一个,自己回去修改自己的想法一致……

但是游戏中,对于这些事件的因果逻辑设计的并没有那么深入,其实也有一部分的原因是因为,这个真的要究其本质其实不过是“galgame”的那种分支选择属性罢了……

阅读全文 »

# 游戏设计

# 会议记录

游戏介绍

概述

跳出盒子工作室制作

是一款卡牌战棋杀戮尖塔like类游戏,打算融合:

  • 战棋
  • 卡牌管理元素
  • 杀戮尖塔like的肉鸽

四个方向的创新

1. 多角色设计&卡牌循环机制创新

创意思考

多角色设计指的是玩家通过操作复数的角色来完成游戏给定的目标,其本质是在有限角色限制下的角色行为规划。

在很多游戏中,设计多角色设计往往是为了让玩家合理规划不同角色的合作,从而保护目标建筑,或者攻破目标单位。

但是常规的这种设计较为无聊,让我们进一步创新,假设需要保护的对象就是英雄本身……甚至更进一步,需要保护的对象和抽牌堆弃牌堆相关联……


具体而言:

两个主单位,两个从单位,主单位控制两个能力”抽牌堆“”弃牌堆“,弃牌堆炸了之后,置入弃牌堆的牌会被散落。而此时,获取卡牌需要使用移动过去,但是同时,为了调节 卡牌 和 移动 的 矛盾我们可以增加一个拾取范围。

我的看法:
1
2
3
4
5
6
7
8
9
title:我的思考:无用的设计

将主单位作为需要保护的对象,承担了抽牌堆弃牌堆的职权,听起来很酷,但是实际上,对于这两个主对象的操作是有害而无利的。

角色每次移动需要使用一张移动卡,而移动只能移动一格,假设玩家的拾取范围是伽利略距离5格,那么玩家实际上消耗一张卡进行移动,在总卡数20/总地块64的情况之下,玩家实际上即使是对于全部散落的抽卡堆,也只能够拿到2张牌,而且这两张牌在若不是自己所需要的牌,则又会重新散落在场地上……

最终会导致的问题就是,卡牌散落在草地上,玩家每次移动只能拿到少数牌,而如果所需要的核心牌被随机到了最远端,则你这一局基本上就不要再想拿到了。

综上所述,玩家面临的实际情况就是,这两个主单位一旦被击败,给玩家带来的也不过是苟延残喘的机会,而这一延展方式,就导致其根本就不会成为游戏中和还不如主单位死了之后直接任务失败……
阅读全文 »

# 杂项技术

在网上购买的u盘往往会存在是扩容盘甚至是扩容TF卡的可能,对于这种U盘,最可恨的还不是其造假的纸面数据,而是其可能会带来的数据损失的风险。

下面简单介绍一下如何检测扩容盘以及如何修复扩容盘

扩容盘的检测

扩容盘的检测需要使用专业的工具,使用360或者电脑管家之类的工具往往是测不确的。我们一般可以通过两种方式来判断其是否是扩容盘:

  1. 通过检测芯片型号来确定其真实的大小
  2. 通过暴力填满来确定其真实的大小

通过检测芯片型号来确定其真实的大小

这是最简单的方式,但是也是最容易出错的方法之一。因为可以通过伪造芯片型号的技术日益高超,使得仅仅依靠型号检测来确认芯片的真实性变得不再可靠。

通过暴力填满来确定其真实的大小

一般会使用 mydisktest 来检测(图吧工具箱里面有)
这种方法是通过将U盘填满文件,以此来确定U盘的实际容量。这种方法虽然较为可靠,但耗时较长。

具体操作原理如下:

  1. 将文件持续写入u盘中
  2. 记录下所添加的文件的总大小,并且校验文件的完整性
  3. 通过校验所有文件存储的内容是否完整,我们可以知道u盘实际容量以及哪个片区存在问题。

还原扩容盘

一般有两种办法:

  1. 修改U盘分区
  2. 使用量产工具将U盘主控还原为真实大小

修改U盘分区的方法:

  1. 首先,我们需要下载一个分区工具,如DiskGenius。
  2. 将U盘插入电脑,打开DiskGenius软件,找到U盘所在的磁盘。
  3. 在磁盘列表中,右键点击U盘磁盘,选择“调整分区大小”选项。
  4. 在弹出的窗口中,选择“快速分区”,根据需要进行调整。
  5. 确认调整后,点击“保存更改”按钮,然后安全弹出U盘。

使用量产工具

  1. 查看U盘使用的芯片型号,可以使用这个软件:chipgenius(图吧工具箱-外设工具里面有)
  2. 下载对应的量产工具,可以查看网站:U盘量产工具,U盘量产工具下载大全_U盘量产网 (upantool.com)
  3. 将U盘插入电脑,运行量产工具,选择相应的U盘型号和量产方案。
  4. 在量产工具中,找到“容量调整”或“分区调整”选项,根据需要进行调整。
  5. 确认调整后,点击“开始量产”按钮,等待量产过程完成。
  6. 量产完成后,安全弹出U盘,即可看到U盘的实际容量。
阅读全文 »

[[pytorch:张量(tenser)]]

问题阐述

python的实现逻辑和内存回收机制挺奇怪的。实际上,当使用tensor类型的时候,其更像是存储了一个指针:

  • 创建变量的时候,先申请分配内存,然后再将变量指向其存储tensor的位置
  • 计算的时候,实际上是不是将其对应的值赋予过去,而是将位置赋予过去。

这就导致了一个问题:

1
2
3
before = id(x)
x = x + y
before == id(x)

输出:false

实际上,计算x+y的时候,系统先申请了一个内存空间,然后将计算结果放在这个空间内,最后将 x存储的位置改为这个内存空间的地址……

所有的变量都是存储在内存中的,如果x释放出来的内存没有被及时释放,这样就会带来很大的内存占用冗余,而这在本就对内存占用需求比较大的深度学习来说是致命的。

解决办法

所以说可以换一种写法,就可以使得其代码效率优化:

1
z[:] = x + y

这样的话,相当于将值进行了复写,而不是重复申请更换空间。而x+y产生的临时内存占用将会被赋值完之后被释放。

如果说,我们知道后面x中存储的数据不再会被使用了,也可以使用下面的写法:

1
x += y

[[pytorch]]

基础知识

导入

首先,我们导入torch

请注意,虽然它被称为PyTorch,但是代码中使用torch而不是pytorch

import torch

定义

张量表示一个由数值组成的数组,这个数组可能有多个维度

  • 具有一个轴的张量:向量(vector)
  • 具有两个轴的张量:矩阵(matrix)
  • 具有两个轴以上的张量没有特殊的数学名称

初始化声明

torch提供的接口

使用 arange 创建
  •   - x = torch.arange(12)
    
      - x
    
      - Copy to clipboard
    
      - tensor([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
    
    • 这个行向量包含以0开始的前12个整数,它们默认创建为整数

    • 也可指定创建类型为浮点数

    • 张量中的每个值都称为张量的 元素(element)

      • 例如,张量 x 中有 12 个元素
    • 除非额外指定,新的张量将存储在内存中,并采用基于CPU的计算

全1/全0初始化张量
    • 全0

        - torch.zeros((2, 3, 4))
      
        - __________________
      
        - tensor([[[0., 0., 0., 0.],
      
        -          [0., 0., 0., 0.],
      
        -          [0., 0., 0., 0.]],
      
        -         [[0., 0., 0., 0.],
      
        -          [0., 0., 0., 0.],
      
        -          [0., 0., 0., 0.]]])
      
    • 全1

        - torch.ones((2, 3, 4))
      
        - __________________
      
        - tensor([[[1., 1., 1., 1.],
      
        -          [1., 1., 1., 1.],
      
        -          [1., 1., 1., 1.]],
      
        -         [[1., 1., 1., 1.],
      
        -          [1., 1., 1., 1.],
      
        -          [1., 1., 1., 1.]]])
      
随机/概率分布 初始化张量
    • 创建一个形状为(3,4)的张量,其中的每个元素都从均值为0、标准差为1的标准高斯分布(正态分布)中随机采样

       - >>>>>>>>>>>>>>>>>
      
       - torch.randn(3, 4)
      
       - ______________________
      
       - tensor([[-0.0135,  0.0665,  0.0912,  0.3212],
      
       -         [ 1.4653,  0.1843, -1.6995, -0.3036],
      
       -         [ 1.7646,  1.0450,  0.2457, -0.7732]])
      
使用包含数值的Python列表(或嵌套列表),来为所需张量中的每个元素赋予确定值
  • 在这里,最外层的列表对应于轴0,内层的列表对应于轴1

    • torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])


    • tensor([[2, 1, 4, 3],

    •     [1, 2, 3, 4],
      
    •     [4, 3, 2, 1]])
      

属性

通过张量的shape属性来访问张量沿每个轴的长度
- x.shape
如果只想知道,即形状的所有元素乘积,可以检查它的大小(size)
  • 因为这里在处理的是一个向量,所以它的shape与它的size相同
通过张量的numel()属性来访问张量中元素的总数
- x.numel()

变换

调用reshape函数改变一个张量的形状而不改变元素数量和元素值
    • 例如,可以把张量x从形状为(12,)的行向量转换为形状为(3,4)的矩阵

    • 这个新的张量包含与转换前相同的值,但是它被看成一个3行4列的矩阵

    • 要重点说明一下,虽然张量的形状发生了改变,但其元素值并没有变

    • 注意,通过改变张量的形状,张量的大小不会改变

    • X = x.reshape(3, 4)

    • X

    • Copy to clipboard

    • 我们不需要通过手动指定每个维度来改变形状

      • 幸运的是,我们可以通过-1来调用此自动计算出维度的功能

      • 即我们可以用x.reshape(-1,4)或x.reshape(3,-1)来取代 x.reshape(3,4)

我的思考

tenser加法存在的问题

![[tenser加法存在的问题]]

阅读全文 »

# python

# d2l

基础数据结构

[[pytorch:张量(tenser)]]

图像处理 torchvision 库

torchvision.transforms

![[torchvision.transforms]]

阅读全文 »
0%