diff --git a/Core/src/LuaModule/gdebugengine.pkg b/Core/src/LuaModule/gdebugengine.pkg index d99982d2..35c6c75e 100644 --- a/Core/src/LuaModule/gdebugengine.pkg +++ b/Core/src/LuaModule/gdebugengine.pkg @@ -6,7 +6,7 @@ $#include "GDebugEngine.h" class CGDebugEngine{ CGDebugEngine(); ~CGDebugEngine(); - void gui_debug_x(const CGeoPoint& p, int debug_color = 1, int RGB=0); + void gui_debug_x(const CGeoPoint& p, int debug_color = 1, int RGB=0, const int size = 60 /*mm*/); void gui_debug_line(const CGeoPoint& p1, const CGeoPoint& p2, int debug_color = 1, int RGB=0); void gui_debug_arc(const CGeoPoint& p, double r, double start_angle, double span_angle, int debug_color = 1, int RGB=0); void gui_debug_triangle(const CGeoPoint& p1, const CGeoPoint& p2, const CGeoPoint& p3, int debug_color = 1, int RGB=0); diff --git a/Core/tactics/play/TestMyRun.lua b/Core/tactics/play/TestMyRun.lua index 43e59217..38f15bad 100644 --- a/Core/tactics/play/TestMyRun.lua +++ b/Core/tactics/play/TestMyRun.lua @@ -1,8 +1,14 @@ local testPos = { - CGeoPoint:new_local(3000, 3100), - CGeoPoint:new_local(-3000, 3100), - CGeoPoint:new_local(-3000,-3100), - CGeoPoint:new_local(3000,-3100) + CGeoPoint:new_local(800, 800), + CGeoPoint:new_local(-800, 800), + CGeoPoint:new_local(800,-800), + CGeoPoint:new_local(-800,-800) +} +local tPos = { + CGeoPoint:new_local(2000, 1500), + CGeoPoint:new_local(-2000, 1500), + CGeoPoint:new_local(2000,-1500), + CGeoPoint:new_local(-2000,-1500) } local vel = CVector:new_local(0, 0) local maxvel = 0 @@ -13,23 +19,50 @@ local DIR = function() return (player.pos('Assister') - ball.pos()):dir() end +local localMatch = "[ALDM]" -- ① +-- local localMatch = "[A][LDM]" -- ② +-- local localMatch = "[AL][DM]" -- ③ +-- local localMatch = "[ALD][M]" -- ④ + +local drawDebug = function() + for i = 1, 4 do + local pos = testPos[i] + debugEngine:gui_debug_msg(pos+Utils.Polar2Vector(40,math.pi/4), string.format("(%3d,%3d)", pos:x(), pos:y()),0,0,50) + debugEngine:gui_debug_x(pos,param.GREEN,0,30) + local pos2 = tPos[i] + debugEngine:gui_debug_msg(pos2+Utils.Polar2Vector(40,math.pi/4), string.format("(%3d,%3d)", pos2:x(), pos2:y()),0,0,50) + debugEngine:gui_debug_x(pos2,param.GREEN,0,30) + + -- debugEngine:gui_debug_line(pos, pos2,param.GREEN) + end + debugEngine:gui_debug_msg(CGeoPoint:new_local(-300,900), localMatch,0) +end + return { - firstState = "skill", - ["skill"] = { + firstState = "ready", + + ["ready"] = { switch = function() + if bufcnt(true,180) then + return "run" + end end, - Leader = {CircleRun{pos=CGeoPoint(0,100), rotVel=4}}, - match = "[L]" + Assister = task.goSimplePos(tPos[1],0), + Leader = task.goSimplePos(tPos[2],0), + Defender = task.goSimplePos(tPos[3],0), + Middle = task.goSimplePos(tPos[4],0), + match = "[ALDM]" }, - ["run1"] = { + + ["run"] = { switch = function() - if bufcnt(player.toTargetDist("a")<5,time) then - return "run"..2 - end + -- drawDebug() end, - a = task.goCmuRush(testPos[1],0, _, DSS_FLAG), - b = task.goCmuRush(testPos[1]+Utils.Polar2Vector(1000,-math.pi/2),0, _, DSS_FLAG), - match = "{ab}" + Assister = task.goSimplePos(testPos[1],0), + Leader = task.goSimplePos(testPos[2],0), + Defender = task.goSimplePos(testPos[3],0), + Middle = task.goSimplePos(testPos[4],0), + match = localMatch }, ["run2"] = { switch = function() diff --git a/doc/img/1_3_1_match_compare.png b/doc/img/1_3_1_match_compare.png new file mode 100644 index 00000000..a6a1f10a Binary files /dev/null and b/doc/img/1_3_1_match_compare.png differ diff --git a/doc/img/1_3_1_match_debug.png b/doc/img/1_3_1_match_debug.png new file mode 100644 index 00000000..765f7d05 Binary files /dev/null and b/doc/img/1_3_1_match_debug.png differ diff --git a/doc/img/1_3_1_match_rule.png b/doc/img/1_3_1_match_rule.png new file mode 100644 index 00000000..f05afdd9 Binary files /dev/null and b/doc/img/1_3_1_match_rule.png differ diff --git a/doc/posts/1_rocos_basic/1_3_1_rule_match.md b/doc/posts/1_rocos_basic/1_3_1_rule_match.md index 30203792..d5ed6f04 100644 --- a/doc/posts/1_rocos_basic/1_3_1_rule_match.md +++ b/doc/posts/1_rocos_basic/1_3_1_rule_match.md @@ -1 +1,63 @@ -# Play - 角色匹配规则[TODO] \ No newline at end of file +# Play - 角色匹配规则 + +在运行过脚本后,我们深入探索一下角色的匹配规则。 + +:::{admonition} 动机 +对于小型足球这样的多智能体场景中,我们在最上层进行简单的状态调转后,最终会执行一个由**多个任务**组成的特定状态对应的**任务包**,在这个任务包中,包含了不定数量的**角色**以及每个角色的**具体任务**,但在此时,我们并不知道每个角色对应的机器人编号是多少,有时我们只希望由距离最近的机器人去执行该任务(代价最小),有时我们又希望由某个特定编号的机器人去执行该任务,这时匹配规则就会起到作用。 +::: + +让我们现在书写一个简单的单状态的Play,使用四种不同的匹配规则,分别是①`[ALDM]`、②`[A][LDM]`、③`[AL][DM]`、④`[ALD][M]`来比较匹配规则的实际作用。 + +:::{card} 简单的角色匹配测试 +```{code-block} lua +:linenos: +local runPos = { + CGeoPoint:new_local(300, 300), + CGeoPoint:new_local(-300, 300), + CGeoPoint:new_local(300,-300), + CGeoPoint:new_local(-300,-300) +} + +gPlayTable.CreatePlay{ + firstState = 'run', + ["run"] = { + switch = function() + end, + Assister = task.goSimplePos(runPos[1],0), + Leader = task.goSimplePos(runPos[2],0), + Defender = task.goSimplePos(runPos[3],0), + Middle = task.goSimplePos(runPos[4],0), + match = "[ALDM]" -- ① + -- match = "[A][LDM]" -- ② + -- match = "[AL][DM]" -- ③ + -- match = "[ALD][M]" -- ④ + }, + name = "TestRun", +} +``` +::: +让四个机器人运行到一个特殊的初始位置后分别以不同匹配规则运行上述状态,可以得到如下结果: +```{thumbnail} ../../img/1_3_1_match_compare.png +``` +上述的匹配规则中使用了`[]`中括号,这代表了**实时匹配**,每个角色在匹配规则中使用首字母表示,例如`A`代表`Assister`,`L`代表`Leader`。匹配采用从左到右的规则,例如: +* `[ALDM]`代表将四个任务角色`Assister`、`Leader`、`Defender`、`Middle`直接进行匹配,由于测试时共有四台机器人,所以每个角色会匹配到一台机器人,并保证四个机器人运动的总代价最小。 +* `[A][LDM]`表示,在四台机器人中,先选择执行`Assister`任务代价最小(例如距离最近)的机器人优先匹配`A`任务,再将剩下三台机器人与三个任务进行分配。 + +上面用到了匹配规则中的两个功能:**分组匹配**和**实时匹配**。为了应对更复杂的场景,我们还设计了其他匹配,完整的描述如下: +```{thumbnail} ../../img/1_3_1_match_rule.png + :width: 70% + :align: center +``` +* `{ } : 保持匹配` - 保持之前机器人匹配,除非当前帧刚经历过**脚本跳转**(新脚本所以无匹配号码)。 +* `( ) : 单次匹配` - 保持之前机器人匹配,除非当前帧刚经历过**状态跳转**(switch函数有返回值即可)。 +* `[ ] : 实时匹配` - 在任何状态下都进行匹配。 + +在具体匹配算法中,我们使用二分图匹配的经典方法-匈牙利算法来实现,算法细节不做展开,但对于N个机器人执行M个任务时,可以勾选面板的`Debug/RoleMatch`来查看用于匹配运算的代价矩阵。下图中每个黄色的数字代表了一个机器人执行一个任务的代价。 +```{thumbnail} ../../img/1_3_1_match_debug.png + :width: 70% + :align: center +``` + +在实际使用时,可能由于各种原因导致机器人数量与任务数量不等的情况: +* 如果机器人数量比任务数量多,会出现有些机器人没有角色可用,现象是机器人原地不动,Client软件上机器人没有类似于`L`,`A`这样的角色标记。 +* 如果机器人数量少于任务数量,会存在部分角色任务没有对应的执行机器人,具体哪个任务缺失由匹配规则决定。所以在书写匹配规则时也需要考虑缺少机器人的情况,有时会使用闭包函数来动态生成匹配规则。 \ No newline at end of file