{"id":16179690,"url":"https://github.com/mivinci/moba","last_synced_at":"2026-04-26T12:31:42.876Z","repository":{"id":197683843,"uuid":"699108319","full_name":"mivinci/moba","owner":"mivinci","description":"Matchmaking simulation implemented in C and Lua.","archived":false,"fork":false,"pushed_at":"2023-10-04T10:34:13.000Z","size":13,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-07T11:47:32.638Z","etag":null,"topics":["c","lua","matchmaking","moba-games"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mivinci.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-01T23:46:32.000Z","updated_at":"2024-12-16T14:30:23.000Z","dependencies_parsed_at":"2024-11-02T20:03:07.479Z","dependency_job_id":"7d90b649-fd30-4dc9-9b18-6feb1d62b833","html_url":"https://github.com/mivinci/moba","commit_stats":null,"previous_names":["mivinci/moba"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mivinci/moba","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mivinci%2Fmoba","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mivinci%2Fmoba/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mivinci%2Fmoba/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mivinci%2Fmoba/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mivinci","download_url":"https://codeload.github.com/mivinci/moba/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mivinci%2Fmoba/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32297893,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T09:34:17.070Z","status":"ssl_error","status_checked_at":"2026-04-26T09:34:00.993Z","response_time":129,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","lua","matchmaking","moba-games"],"created_at":"2024-10-10T05:43:46.042Z","updated_at":"2026-04-26T12:31:42.861Z","avatar_url":"https://github.com/mivinci.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MOBA 游戏组队/匹配模拟\n\n大致思路就是将匹配的过程抽象为某个数据结构和一系列函数，部分函数里需要热更新的部分交给 Lua 运行时调用可在运行期替换的 Lua 代码。代码规范上也偏爱和遵循 Lua 的风格。另外，抽象出的数据结构里的*装载因子*的设计比较巧妙，算是原创（应该是吧？\n\n\n## 设计综述\n\n先总结并完善下需求：\n\n- 按照 段位、排位分、擅长的路线 三个因素来尽可能公平地匹配队友或对手\n- 匹配逻辑和相关配置交给嵌入的 Lua 脚本，实现运行期热更新\n- 预组队存在队员人数不够的话，要跟池子里其他队伍合并，满足 5 人开黑条件\n- 匹配度和匹配等待时间取舍的动态调整\n- 匹配对手后允许队内换位，换位可能使得队伍的加权排位分更新，由此来模拟选/禁英雄的博弈阶段\n\n\n### 结构体\n\n**玩家**\n\n```c\nstruct player {\n  int id;  // starts from 1\n  int rank;\n  int score;\n  int role;  // role bitmap\n};\n```\n\n\n`player::id` 表示玩家 id 且唯一。\n\n`player::rank` 表示玩家段位，整型，取值范围为 [0, 5]，分别对应段位名称：\n\n- BRONZE     \n- SILVER     \n- GOLD       \n- PLATNUM    \n- DIAMOND    \n- CHALLENGER\n\n`player::score` 表示玩家的排位分，整型。\n\n`player::role` 表示玩家擅长的路线，位图。\n\n使用位图的原因有两点：\n\n1. 用一个整型数字就能表示出玩家擅长的所有路线\n2. 玩家进行组队时，可以通过对该字段进行简单的或运算，得出该队伍的路线分配情况，可作为匹配因素\n\n所以，从低位到高位，分别对应路线：\n\n- TOP 上路\n- BOT 下路\n- MID 中路\n- JG  打野\n- SUP 辅助\n\n例如，若玩家 p1 的 role 为 0，则表示玩家 p1 要打上路，跟 role 为 6 的玩家 p2 组队，则该队伍的 role 就为 (p1.role | p2.role) = 7，即该队伍覆盖了上下中路，还差打野和辅助，在匹配队友的时候就可以再通过或运算找到 role 为 24 的队伍进行合并。\n\n**队伍**\n\n```c\nstruct group {\n  int len;\n  int cap;\n  struct player *players;\n  struct list_head node;\n};\n```\n\n`group::len` 表示预组队人数，整型。\n\n`group::cap` 表示该队伍要打的是几人开黑局，整型。\n\n`group::players` 记录队员和站位，该字段预分配的长度为 `group::cap`。\n\n`group::node` 链表节点，参考 Linux 内核循坏双链表 `list.h` 使用\n\n**匹配池**\n\n```c\nstruct moba {\n  lua_State *L;\n  struct list_head *queues;  // matchmaking queues\n  int len;\n  int n;\n  int k;\n};\n```\n\n`moba::L` Lua 运行沙盒。\n\n`moba::queues` 不同人数队伍的匹配池，链表数组。\n\n\n`moba::len` 已合并的预组队队伍的个数，整型。\n\n`moba::n` 每个队伍需要的人数，整型。若为 5 则表示为 5 排匹配池。\n\n`moba::k` 匹配池的*装载因子*，整型。\n\n装载因子用来控制匹配队友等待时间，装载因子越大，等待的时间就越久，但找到更合适的队友的概率就越大。\n\n\n\n### 函数\n\n这里介绍几个核心函数和实现原理。\n\n```c\nstruct moba *moba_open(int n, int k);\n```\n\n创建并返回一个匹配池。例如，`moba_open(5, 32)` 表示创建一个 5 黑匹配池，装载因子为 32。\n\n```c\nvoid moba_load(struct moba *M, const char *name);\n```\n\n加载一个 Lua 脚本，参数 `name` 为要加载的 Lua 脚本的路径。所以系统检测到 Lua 脚本更新时，可以重新调用函数实现运行期热更新匹配逻辑的实现。\n\n```c\nvoid moba_push(struct moba *M, struct group *g);\n```\n\n将一个预组队队伍加入对应的匹配池。`moba::queues` 用链表数组维护了不同人数的匹配池，如下图所示\n\n```\n+---+   \n| 1 |--\u003e g13 --\u003e g12 --\u003e g11 --\u003e g10\n+---+   \n| 2 |--\u003e g20\n+---+\n| 3 |--\u003e g31 --\u003e g30\n+---+\n| 4 |--\u003e g42 --\u003e g41 --\u003e g40\n+---+\n| 5 |--\u003e g50 --\u003e g51\n+---+\n```\n\n假设我们现在要将一个人数为 4 的预组队队伍 g43 加入匹配池，那么有两种情况：\n\n1. 若 5 人匹配池已满（达到装载因子大小），则直接将 g43 插入 4 人匹配池。\n2. 若 5 人匹配池未满（未达到装载因子大小），则遍历 1 人匹配池，找到与 g43 最匹配的 1 人队伍，将他们合并后插入 5 号匹配池。\n\n这样便巧妙地实现了通过装载因子 k 来控制匹配队友等待时间。\n\n```c\nint moba_match(struct moba *M, struct match *mat);\n```\n\n在匹配池里找到人数已达到开黑要求的两只队伍，将他们放到一个 `struct match` 结构体中，若匹配到，则返回 0，否则返回 -1。`struct match` 的定义如下：\n\n```c\nstruct match {\n  struct group *red;\n  struct group *blue;\n};\n```\n每调用一次 `moba_match` 就会进行一次对手匹配，所以系统可以通过调正该函数的调用频率来控制匹配的速度。该函数调用的频率越小，匹配池里玩家的等待时间就越长，但找到更合适的队友和对手的概率就越大。\n\n综上，通过对**装载因子**和调用 `moba_match` 的**频率**的控制来实现对**匹配度**和**匹配等待时间**的动态调整。\n\n## 示例\n\n### 依赖\n\n需要先下载 Lua 库\n\n- Archlinux\n\n```bash\npacman -S lua\n```\n\n- Debian\n\n```bash\napt install lua\n```\n\n- RHEL\n\n```bash\ndnf install lua\n```\n\n- macOS\n\n```bash\nbrew install lua\n```\n\n### 编译\n\n直接 make 就行，Makefile 里写好了完整的编译流程。\n\n```bash\nmake\n```\n\n### 运行\n\n准备一些预组队队伍信息，写入 [input.txt](./input.txt)。\n\n**输入描述**\n\n1. 第一行两个整数 n, m，分别表示队数和每队需要的开黑人数\n2. 接下来的 n 部分，每部分第一行一个整数 k，表示预组队人数\n3. 每部分后 k 行里每行 3 个整数，分别表示玩家的段位、排位分、擅长路线\n\n然后运行示例：\n\n```bash\nmake test\n```\n\n**输出描述**\n\n如下图所示，前 5 行表示双方每位队员的站位和每个队员的排位分，第 6 行为双方的队伍加权平均排位分，第 7 行表示双方的 ELO 值。ELO 值的计算方式也可以在 [test.lua](./test.lua) 中更改。 \n\n```\n        BLUE    RED\nTOP     1(10)   7(12)\nBOT     2(20)   8(20)\nMID     3(10)   9(30)\nJG      4(30)   6(20)\nSUP     5(20)   10(13)\nSCORE   17.0    18.9\nELO     0.50    0.50\n1 pair(s) of groups matched\n```\n\n\n\n\n### 匹配逻辑\n\n匹配逻辑实现在 [test.lua](./test.lua) 里的 `match` 函数中，匹配队友和对手都是通过该函数实现，例如按段位、排位分、擅长路线综合匹配：\n\n```lua\nfunction match(g1, g2)\n  -- 若段位差超过 delta_rank 则匹配失败\n  local rank_min1, rank_max1 = moba.rank(g1)\n  local rank_min2, rank_max2 = moba.rank(g2)\n  if ((rank_max1 - rank_min2) \u003e delta_rank) or ((rank_max2 - rank_min1) \u003e delta_rank) then\n    return false\n  end\n\n  -- 若路线覆盖没有达到 delta_role 则匹配失败\n  local role1 = moba.role(g1)\n  local role2 = moba.role(g2)\n  if (bitcount5(role1 | role2) \u003c delta_role) then\n    return false\n  end\n\n  -- 若 score 之差超过 delta_score 则匹配失败\n  local s1 = score(g1)\n  local s2 = score(g2)\n  return math.abs(s1 - s2) \u003c delta_score\nend\n```\n\n## 参考\n\n- https://en.wikipedia.org/wiki/Elo_rating_system\n- https://ai.plainenglish.io/exploring-skill-based-matchmaking-systems-in-online-games-96491816d9e7\n- https://segmentfault.com/q/1010000009504187\n- https://www.youtube.com/watch?v=-pglxege-gU\n- https://github.com/cloudwu/skynet\n- https://www.lua.org/manual/5.1\n- *Programming in Lua - 4th edition*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmivinci%2Fmoba","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmivinci%2Fmoba","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmivinci%2Fmoba/lists"}