我的博客

Tactics Core 小游戏(炎龙骑士团)

目录
  1. 可行的策略
  2. 隐藏元素

大概是上小学的时候玩过这个游戏,当时是在“爱威海”网站的小游戏频道里找到的这个游戏(当时一个同学告诉我这个网站的,那时候家里也是刚买了电脑,喜欢找各种有趣的网站看)。当时记得周五放学回家就打开电脑,看看爱威海上的小游戏有没有更新。现在爱威海已经没有小游戏频道了。当时就特别喜欢这个游戏,操作简单,看不懂游戏里的英语也学会了。开始打不过电脑,感觉电脑的人物都好厉害,Mystical Sorceress(对方的老大)可以同时攻击多个不相邻的人(我方老大只能攻击一个十字上的人,可能误伤自己人),我方医生只能治疗相邻的人,对方医生却可以治疗远处的人。但是实际上我方的角色看起都比对方弱一点,但是我方比对方多一人(电脑 6 个人,我方有 7 个人)。

游戏的开发商是 Digital Deed Entertainment,游戏开始页面上的简介中说,这个游戏是一个用于演示Tactics Core 引擎的例子(这个引擎大概就是他们公司的产品)。他们官网上也有几个他们开发的其他游戏。

他们的官网上也有这个游戏的简介:http://www.digisonline.com/tacticscore/,也提供了在线试玩的链接,但是在我的浏览器里不工作,4399上也有这个游戏,4399上这个游戏名称被翻译为炎龙骑士团(链接:http://www.4399.com/flash/37906_3.htm)。

我的视频中展示了一种策略,可以保证全部角色存活,并且有两个角色未受到伤害(医生和老大)。视频链接:https://www.bilibili.com/video/BV1vD4y1Q7Pb/

可行的策略

其实我方本身人数比对方多,铁剑哨兵(Ironclad Sentinal)血量和防御都高,不太容易死(但是其他角色都比较脆)。

如果想所有角色都存活有如下策略:

  1. 开始先集中火力打死或打残一个人,我一般选择敌方红色剑客(Macabre Swordsman)或者蓝色剑客(Talon Soldier)如我的视频里,是先打红色剑客,如果运气好,一轮攻击就能打死,运气不好(如我视频中的情况)一轮后,加上我方刺客一次攻击也能打死了。
  2. 然后打死对方的老大。
  3. 可以用移动速度快的角色把敌方射手逼入地图上方的角落,只要他进去了一般就只有一次攻击机会,因为你紧挨着他时他无法攻击。
  4. 如果想减少我方角色死亡应该避免我方角色被集中火力,要让伤害分布均匀。

隐藏元素

如果我方除了医生(Mystical Healer)以外所有角色都死亡了,那么医生会变成Daemon,血量是 ????,但是实际血量是有限的,感觉和普通角色差距不大,由于她速度慢,如果敌人太多还是会被打死。

image.png

image.png

她一次能走6步

image.png

攻击范围是4,每次攻击一个格子。她每次攻击造成的伤害都是 9999,一次可以秒杀任意一个范围内的敌人。

image.png

MacOS上使用Selenium控制Safari

目录

苹果官方文档:

https://developer.apple.com/documentation/webkit/about_webdriver_for_safari

https://developer.apple.com/documentation/webkit/testing_with_webdriver_in_safari

  1. 首先要确认 safaridriver 已经安装,路径在 /usr/bin/safaridriver

  2. 然后修改 Safari 配置允许自动控制

    image.png

    先在配置中显示 Develop 菜单,然后到 Develop 菜单中开启运行远程自动控制

    image.png

  3. 安装 selenium

    pip3 install selenium

  4. 使用 selenium 启动浏览器

    1
    2
    3
    4
    from selenium import webdriver

    driver = webdriver.Safari()
    driver.get('https://codeplot.top/')

在Windows10上安装MacOS虚拟机(VirtualBox + AMD ryzen)

目录
  1. 安装依赖
  2. 开始安装

试了很多方法都失败了,最后找到了 https://github.com/myspaghetti/macos-virtualbox/

尝试使用 cywin 但是卡在这里了:

1
2
3
Please make sure the following packages are installed:
coreutils gzip unzip xxd wget
Please make sure the coreutils and gzip packages are the GNU variant.

然后尝试WSL,结果很好用

安装依赖

  1. unzip

    sudo apt install unzip

  2. dmg2image

    1
    2
    3
    sudo add-apt-repository universe
    sudo apt update
    sudo apt install dmg2img
  3. Oracle VM VirtualBox Extension Pack

    也是 virtualbox 下载页面下载https://www.virtualbox.org/wiki/Downloads

开始安装

git clone https://github.com/myspaghetti/macos-virtualbox.git

进入该目录后执行

1
2
chmod +x macos-guest-virtualbox.sh
./macos-guest-virtualbox.sh

可能是因为 AMD CPU 的问题,开机卡住了,最后一行是 EXITBS 之类的。

然后参考https://github.com/myspaghetti/macos-virtualbox/issues/8 解决,关闭虚拟机,到 virtual box 的安装目录执行 VBoxManage modifyvm "macOS" --cpu-profile "Intel Core i7-2635QM" 再次开机就可以了。

搭建局域网视频直播服务:使用 node media server

目录
  1. 服务器配置
  2. 直播端配置
  3. 客户端

推流软件(直播视频采集):OBS Studio,一款免费的开源软件,支持 Window,Linux 和 MacOS

直播服务器:nodejs 的 node media server(nms)

Web 端视频播放器:flv.js

直播端和服务器可以是同一台机器。

服务器配置

  1. 安装 nodejs

    访问官方网站: https://nodejs.org/en/

    下载安装包并安装,有 LTS 和 Current 都可以,一般选 LTS

    image.png

  2. 进入目录 nms 执行 install.bat

  3. 执行 run.cmd

  4. 执行 run2.cmd

    此时会显示网址,就是在局域网内可以访问的地址,等配置好直播端,直接访问这个地址就可以观看直播了。

    image.png

直播端配置

  1. 安装 OBS Studio

  2. 配置

    image-20200620155327225

    点击 sources 的加号,选择 Display Capture,这个会捕获整个屏幕的视频。再点击确定。

image.png

然后点击右侧的 Start Streaming 开始推送,但是第一次使用没有配置服务器,会弹出提示,点击打开配置即可

image.png

打开配置后点击左侧第二个 Stream,右边的第一个 Service 选择 Custom(即自定义),Server 填 rtmp://127.0.0.1/live, Stream Key 填 test,然后点 OK。

配置好再次点击 Start Streaming

客户端

最后在同一个局域网的机器上打开上面的网址就可以观看直播了

打卡助手使用说明

目录

这是我业余开发的项目,主要是帮助大家整理乐心运动步数打卡的图片,可以自动识别出步数和日期,并整理出时间线。可以自定义提醒时间,如果到了提醒时间还没有完成打开会自动推送提醒(这个还没有开发完成)。

效果是:

image.png

使用方法:

  1. 关注我的公众号:Colander漏勺

    qrcode_for_gh_71c26a4b3cca_258.jpg

  2. 点击公众号右下角菜单漏勺首页进入首页并注册和登录

  3. (这步可以跳过,不绑定微信也可以使用多数功能,但是无法收到推送提醒)绑定微信,登录后选择微信管理,点击绑定微信,然后才能收到推送提醒

  4. 给公众号发送打卡图片,需要点击公众号底部的切换按钮切到发送消息模式,然后点击加号,发送乐心步数页面打卡图片。

Javascript 键盘事件

目录
  1. 遇到的坑
    1. keyCode 属性
    2. keypress 事件
  2. 总结
    1. keyboard event 属性
    2. JavaScript keyboard event keyCode
    3. 关于事件回调函数

JS 监听键盘事件

遇到的坑

keyCode 属性

keyboardEvent 的 keyCode 属性已经 Deprecated,新的浏览不一定会支持该属性。MDN 文档 建议使用 code 属性

这里有一个开源项目可以把 keyCode 转换为 code:

https://github.com/marijnh/w3c-keycode

https://www.npmjs.com/package/w3c-keycode

但是 keyCode 和 code 不是严格对应的,且不同浏览器,使用输入法等多种情况会影响 keyCode 的值。

keypress 事件

keypress 事件已经 Deprecated,新的浏览器不一定支持该事件。

keypress 事件一般是紧随 keydown 事件,但是 keypress 事件仅对可见字符有效,例如字母,数字,空格符号等,很多控制字符是不会触发该事件的。而且更重要的是,keypress 事件的 keycode 属性和 keyup,keydown 是不同的,它对应的是 ascii 码而不是键的编号。

总结

keyboard event 属性

demo:

1
2
3
4
5
6
7
8
9
10
11
<input>
<script>
let targetEvents = ['keydown', 'keyup','keypress']
let el = [];
targetEvents.forEach( function (s) {
document.addEventListener(s, function (e){
el.push(e)
console.log(e.type, e.keyCode,e. charCode, e.key, e.code);
});
});
</script>

code:IE 浏览器都不支持该属性,值是字符串,例如字母键:”KeyA”, “KeyB”, … “KeyZ”(一律是大写),数字键区分小键盘和和大键盘(小键盘:“Numpad4”, 大键盘:“Digit2”),ctrl shift 区分左右等等(ShiftRight, AltLeft 等),回车可以区分大键盘(Enter)和小键盘(NumpadEnter)。

key:对于可见字符就是该字符本身,控制字符则是这个字符的名称,例如 Enter (无法区分大键盘,小键盘),字母能体现大小写,

charCode

keyCode:整数值,主要是上面提到的 keypress 事件与其他两个事件的值含义不同,keypress 是 ASCII 码,keydown 和 keyup 是一套专门的编码。

JavaScript keyboard event keyCode

大键盘数字 0 ~ 9: 48 ~ 57

小键盘数字 0 ~ 9:96 ~ 105

字母 A - Z : 65 ~ 90

空格:32

回车:13

F1 ~ F24:112 ~ 135

BackSpace: 8

Tab: 9

Caps Lock:20

左上右下:37 ~ 40

关于事件回调函数

IE 浏览器版本 9 以下只能通过 window.event 获取事件对象,而 firefox 在某些情况下不能通过 window.event 获取该对象。

但是如果仅支持 IE9 和以上就可以直接使用回到函数的第一个参数获取事件对象了。

https://www.jianshu.com/p/e8a6fad0f7bc

Windows 获取实时 CPU 使用率

目录
  1. typeperf 命令
  2. wmic 命令
  3. vbs 脚本

https://stackoverflow.com/questions/9097067/get-cpu-usage-from-windows-command-prompt

typeperf 命令

连续输出:

typeperf "\processor(_total)\% processor time"

只输出一次:

typeperf "\Processor(_Total)\% Processor Time" -sc 1

输出:

C:\Users\sxwxs\MyBlog>typeperf “\Processor(_Total)\% Processor Time” -sc 3

“(PDH-CSV 4.0)”,”\DESKTOP-HRM3VGN\Processor(_Total)\% Processor Time”
“05/26/2020 16:48:59.930”,”5.565367”
“05/26/2020 16:49:00.932”,”4.185358”
“05/26/2020 16:49:01.935”,”6.002955”

命令成功结束。

wmic 命令

1
wmic cpu get loadpercentage

连续输出:

1
@for /f "skip=1" %p in ('wmic cpu get loadpercentage') do @echo %p%

输出:

C:\Users\sxwxs\MyBlog>wmic cpu get loadpercentage
LoadPercentage
22

vbs 脚本

https://blog.csdn.net/weixin_33819479/article/details/91870042

1
2
3
On Error Resume Next
Set objProc = GetObject("winmgmts:\\.\root\cimv2:win32_processor='cpu0'")
Wscript.Echo "cpu 使用率: " & objProc.LoadPercentage & "%"

vbs 输出只能是弹出对话框。

参考这篇博客:https://blog.csdn.net/codeh/article/details/5492951

WSH.Echo "print your message here"

然后使用 CScript tmp.vbs 运行脚本,可以输出到命令行。

在 multimodal Twitter dataset 上使用 VL-BERT

目录
  1. 预处理图片数据
  2. 训练
    1. 配置环境
    2. 初始化
    3. 训练
    4. 问题
    5. 测试

VL-BERT 代码:https://github.com/jackroos/VL-BERT/

预处理图片数据

VL-BERT 使用 fast rcnn 提取图片中的物体。

我使用 https://github.com/open-mmlab/mmdetection/ 提供的 resnet 101 fast rcnn 实现

下载模型,准备入口代码

1
2
3
mkdir mycode
cd mycode
wget https://open-mmlab.oss-cn-beijing.aliyuncs.com/mmdetection/models/faster_rcnn_r101_fpn_1x_20181129-d1468807.pth

创建 main.py,内容是:

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
import mmcv
from mmcv.runner import load_checkpoint
from mmdet.models import build_detector
from mmdet.apis import inference_detector, show_result
from tqdm import tqdm
import os
import json

cfg = mmcv.Config.fromfile('configs/faster_rcnn_r101_fpn_1x.py')
cfg.model.pretrained = None

# construct the model and load checkpoint
model = build_detector(cfg.model, test_cfg=cfg.test_cfg)
_ = load_checkpoint(model, '/root/code/faster_rcnn_r101_fpn_1x_20181129-d1468807.pth')

image_list = os.listdir('/root/images')
# test a single image
for name in tqdm(image_list):
if not name.endswith('.jpg'):
continue
img = mmcv.imread('/root/images/%s' % name)
result = inference_detector(model, img, cfg)
rl = []
for r in result:
if r.shape[0] > 0:
for x in r:
rl.append(x.tolist())
with open('/root/features/%s.boxs' % name.split('.')[0], 'w') as f:
f.write(json.dumps(rl))

启动 docker 容器

1
2
docker pull vistart/mmdetection:v0.6.0
docker run --gpus all -v /home/sxw/jupyter_workspace/Data/sarcasm/dataset_image/:/root/images:ro -v /tmp/sarcasm_image2:/root/features -v /home/sxw/jupyter_workspace/mutil-model/mycode:/root/code:ro --rm -it vistart/mmdetection:v0.6.0 /bin/bash

进入容器后

1
2
3
4
pip3 install pycocotools tqdm
cd mmdetection/
cp /root/code/main.py .
python3 main.py

训练

配置环境

1
2
3
4
5
6
7
8
9
10
11
pip install torch==1.1.0

git clone https://github.com/NVIDIA/apex
cd apex
pip install -v --no-cache-dir --global-option="--cpp_ext" --global-option="--cuda_ext" ./

cd ../
git clone https://github.com/sxwxs/VL-BERT.git
cd VL-BERT
pip install Cython
pip install -r requirements.txt

初始化

1
./scripts/init.sh

下载对应的预训练模型

https://github.com/jackroos/VL-BERT/blob/master/model/pretrained_model/PREPARE_PRETRAINED_MODELS.md

训练

1
./scripts/nondist_run.sh twitter/train_end2end.py cfgs/twitter/base_4x16G_fp32.yaml ./out/ 2> err.log

问题

遇到一堆问题

RuntimeError: CUDA error: an illegal memory access was encountered

CUDA_LAUNCH_BLOCKING=1 ./scripts/nondist_run.sh twitter/train_end2end.py cfgs/twitter/base_4x16G_fp32.yaml ./out/ 2> err.log

测试

1
python twitter/test.py  --cfg cfgs/twitter/base_4x16G_fp32.yaml --ckpt out/output/vl-bert/twitter/base_4x16G_fp32/train2014+val2014_train/vl-bert_base_res101_vqa-0004.model --gpus 0 --result-path result_output --result-name test1 2> terr.log

python3 list 和 tuple 性能对比

目录
  1. 代码
    1. 代码一:tuple 版
    2. 代码二:list 版
    3. 代码三:list 版(使用同一个对象)
    4. 代码四
    5. 代码五
    6. 代码六
    7. 代码七
    8. 代码八

tuple 是不可变对象,理论上性能应该比 list 快。但是我在最近的次数据分析中发现,用 tuple 替换 list 反而导致内存和时间的消耗显著增多。所以我做了一个简单的实验。

定义一个 list,然后往 list 中 append 1 亿个 list 或者 tuple,其中的元素都是 1,2,3,4,5 五个数字。

1
2
3
4
5
6
7
8
9
$python3 test_tuple.py
100%|█████████████| 100000000/100000000 [00:23<00:00, 4185364.44it/s]

0.7727 GB

$ python3 test_list.py
100%|█████████████| 100000000/100000000 [01:14<00:00, 1339510.06it/s]

12.9193 GB

tuple 耗时 23 秒,使用内存 0.7727 GB (代码一)

list 耗时 74 秒,使用内存 12.9193 GB(代码二)

通过简单的读取 /proc/meminfo 获得大致的内存消耗,windows 无法运行,还需要安装依赖 tqdm(pip install tqdm

上面的实验是有问题的, 感谢 rainy 的评论,这里 tuple 比 list 快和省内存的原因是编译器优化了 tuple 的代码,每次 append 的 tuple 对象都是同一个,所以实际上这里只创建了一个对象。所以我添加了两个实验。

如 rainy 评论的,list 版在循环外定义一个 x = [1,2,3,4,5,] 每次 append(x), 得到的结果与 tuple 相同

耗时 24 秒,内存消耗 0.748 GB(代码三)

1
2
3
100%|██████████| 100000000/100000000 [00:24<00:00, 4051356.31it/s]

0.7480697631835938

所以说代码一的实际只创建了一个对象。

于是又做了一组实验,append 5 千万次,i 从 0 到 5 千万,每次 append 的是 [i+1, i+2, i+3, i+4, i+5,] 或者 (i+1, i+2, i+3, i+4, i+5,)。这样避免了解释器优化的问题。

1
2
3
4
5
6
7
8
9
10
11
tuple(代码四)

100%|██████████| 50000000/50000000 [00:27<00:00, 1825827.72it/s]

12.5092 GB

list(代码五)

100%|██████████| 50000000/50000000 [00:51<00:00, 970668.78it/s]

14.0284 GB

tuple 比 list 稍好一些

最后我仔细看了下之前的代码,复现了一下我当时遇到的问题。原来我当时是这样做的:[int(s, 16) for s in x], x 是一个数组,元素是 16 进制字符串。我误以为把 [] 换成 () 就从 list 换成 tuple 了,结果实际上

1
2
3
4
>>> [int(s, 16) for s in ['4f', 'e2']]
[79, 226]
>>> (int(s, 16) for s in ['4f', 'e2'])
<generator object <genexpr> at 0x00EFDB18>

换成 () 后得到的是 generator,换成 tuple(int(s, 16) for s in ['4f', 'e2']) 就好了。原来 generator 消耗这么大

实验结果:

1
2
3
4
5
6
7
8
9
10
11
tuple(代码六)
100%|██████████| 5000000/5000000 [00:36<00:00, 136155.14it/s]
1.5345726013183594

list (代码七)
100%|██████████| 5000000/5000000 [00:40<00:00, 124367.94it/s]
1.7481269836425781

generator(代码八,这个是无意中的错误,实际上不应该用 generator)
100%|██████████| 5000000/5000000 [00:53<00:00, 93451.91it/s]
5.271724700927734

代码

代码一:tuple 版

1
2
3
4
5
6
7
8
9
10
11
12
13
from tqdm import tqdm
def get_free_mem():
with open('/proc/meminfo') as f:
f.readline()
f.readline()
return int(f.readline().split()[1])
f1 = get_free_mem()
l = []
for i in tqdm(range(100000000)):
l.append((1,2,3,4,5,)
f2 = get_free_mem()
print(f1-f2)
print((f1-f2)/1024/1024)

代码二:list 版

1
2
3
4
5
6
7
8
9
10
11
12
13
from tqdm import tqdm
def get_free_mem():
with open('/proc/meminfo') as f:
f.readline()
f.readline()
return int(f.readline().split()[1])
f1 = get_free_mem()
l = []
for i in tqdm(range(100000000)):
l.append([1,2,3,4,5,])
f2 = get_free_mem()
print(f1-f2)
print((f1-f2)/1024/1024)

代码三:list 版(使用同一个对象)

1
2
3
4
5
6
7
8
f1 = get_free_mem()
l = []
x = [1,2,3,4,5,]
for i in tqdm(range(100000000)):
l.append(x)
f2 = get_free_mem()
print(f1-f2)
print((f1-f2)/1024/1024)

代码四

1
2
3
4
5
6
7
f1 = get_free_mem()
l = []
for i in tqdm(range(50000000)):
l.append((i+1, i+2, i+3, i+4, i+5,))
f2 = get_free_mem()
print(f1-f2)
print((f1-f2)/1024/1024)

代码五

1
2
3
4
5
6
7
f1 = get_free_mem()
l = []
for i in tqdm(range(50000000)):
l.append([i+1, i+2, i+3, i+4, i+5,])
f2 = get_free_mem()
print(f1-f2)
print((f1-f2)/1024/1024)

代码六

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from tqdm import tqdm
import json

def get_frre_mem():
with open('/proc/meminfo') as f:
f.readline()
f.readline()
return int(f.readline().split()[1])

f = open('/opt/sdb1/bitcoin_raw/index/block_foward_450001_500000')
d = {}
freem = [get_frre_mem()]
for _ in tqdm(range(5000000)):
l = f.readline()
if not l:
print('finish')
break
l = l.split('\t')
d[int(l[0], 16)] = tuple([int(x, 16) for x in json.loads(l[2])])
cur_fm = get_frre_mem()
freem.append(cur_fm)
print((freem[0]-cur_fm)/1024/1024)

代码七

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from tqdm import tqdm
import json

def get_frre_mem():
with open('/proc/meminfo') as f:
f.readline()
f.readline()
return int(f.readline().split()[1])

f = open('/opt/sdb1/bitcoin_raw/index/block_foward_450001_500000')
d = {}
freem = [get_frre_mem()]
for _ in tqdm(range(5000000)):
l = f.readline()
if not l:
print('finish')
break
l = l.split('\t')
d[int(l[0], 16)] = [int(x, 16) for x in json.loads(l[2])]
cur_fm = get_frre_mem()
freem.append(cur_fm)
print((freem[0]-cur_fm)/1024/1024)

代码八

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from tqdm import tqdm
import json

def get_frre_mem():
with open('/proc/meminfo') as f:
f.readline()
f.readline()
return int(f.readline().split()[1])

f = open('/opt/sdb1/bitcoin_raw/index/block_foward_450001_500000')
d = {}
freem = [get_frre_mem()]
for _ in tqdm(range(5000000)):
l = f.readline()
if not l:
print('finish')
break
l = l.split('\t')
d[int(l[0], 16)] = (int(x, 16) for x in json.loads(l[2]))
cur_fm = get_frre_mem()
freem.append(cur_fm)
print((freem[0]-cur_fm)/1024/1024)

比特币链上的重复交易

目录

比特币链上有两个 txid 为 e3bf3d07d4b0375638d5f1db5255fe07ba2c4cb067cd81b84ee974b6585fb468 的交易。它们分别是 91722 和 91880 块的 coinbase 交易

是因为算法缺陷导致 coinbase 交易的 txid 容易重复。

后来被修复了。

BIP 30 introduced a rule that prevented blocks from containing a TXID that already exists.
BIP 34 required coinbase transactions to include the height of the block the were mining in to their transaction data.