Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(match2): Handle submatch/subgroup on hearthstone #5217

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
24 changes: 24 additions & 0 deletions components/match2/wikis/hearthstone/brkts_wiki_specific.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
-- @Liquipedia
-- wiki=hearthstone
-- page=Module:Brkts/WikiSpecific
--
-- Please see https://github.com/Liquipedia/Lua-Modules to contribute
--

local FnUtil = require('Module:FnUtil')
local Lua = require('Module:Lua')
local Table = require('Module:Table')

local BaseWikiSpecific = Lua.import('Module:Brkts/WikiSpecific/Base')

---@class HearthstoneBrktsWikiSpecific: BrktsWikiSpecific
local WikiSpecific = Table.copy(BaseWikiSpecific)

WikiSpecific.matchFromRecord = FnUtil.lazilyDefineFunction(function()
local CustomMatchGroupUtil = Lua.import('Module:MatchGroup/Util/Custom')
return CustomMatchGroupUtil.matchFromRecord
end)


return WikiSpecific
13 changes: 12 additions & 1 deletion components/match2/wikis/hearthstone/match_group_input_custom.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,22 @@ local CharacterStandardization = mw.loadData('Module:CharacterStandardization')
local FnUtil = require('Module:FnUtil')
local Logic = require('Module:Logic')
local Lua = require('Module:Lua')
local Table = require('Module:Table')

local MatchGroupInputUtil = Lua.import('Module:MatchGroup/Input/Util')
local OpponentLibraries = require('Module:OpponentLibraries')
local Opponent = OpponentLibraries.Opponent

local CustomMatchGroupInput = {}
local MatchFunctions = {}
local MapFunctions = {}
MatchFunctions.OPPONENT_CONFIG = {
resolveRedirect = true,
pagifyTeamNames = true,
pagifyPlayerNames = true,
}
local MapFunctions = {
ADD_SUB_GROUP = true,
}

---@param match table
---@param options table?
Expand Down Expand Up @@ -53,6 +56,14 @@ function MatchFunctions.getBestOf(bestofInput)
return tonumber(bestofInput)
end

---@param match table
---@param games table[]
---@param opponents table[]
---@return table
function MatchFunctions.getExtraData(match, games, opponents)
return Table.filterByKey(match, function(key) return key:match('subgroup%d+header') end)
end

---@param match table
---@param map table
---@param opponents table[]
Expand Down
167 changes: 167 additions & 0 deletions components/match2/wikis/hearthstone/match_group_util_custom.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
---
-- @Liquipedia
-- wiki=hearthstone
-- page=Module:MatchGroup/Util/Custom
--
-- Please see https://github.com/Liquipedia/Lua-Modules to contribute
--

local Array = require('Module:Array')
local Logic = require('Module:Logic')
local Lua = require('Module:Lua')
local String = require('Module:StringUtils')
local Table = require('Module:Table')

local MatchGroupUtil = Lua.import('Module:MatchGroup/Util')
-- can not use `Module:OpponentLibraries`/`Module:Opponent/Custom` to avoid loop
local Opponent = Lua.import('Module:Opponent')

local CustomMatchGroupUtil = Table.deepCopy(MatchGroupUtil)

---@class HearthstoneMatchGroupUtilSubmatch
---@field games MatchGroupUtilGame[]
---@field opponents GameOpponent[]
---@field resultType ResultType
---@field scores table<number, number>
---@field subgroup number
---@field walkover WalkoverType
---@field winner number?
---@field header string?

---@class HearthstoneMatchGroupUtilMatch: MatchGroupUtilMatch
---@field submatches StormgateMatchGroupUtilSubmatch[]?
---@field isTeamMatch boolean

---@param record table
---@return HearthstoneMatchGroupUtilMatch
function CustomMatchGroupUtil.matchFromRecord(record)
local match = MatchGroupUtil.matchFromRecord(record) --[[@as HearthstoneMatchGroupUtilMatch]]

-- Adjust game.opponents by looking up game.opponents.players in match.opponents
Array.forEach(match.games, function(game)
game.opponents = CustomMatchGroupUtil.computeGameOpponents(game, match.opponents)
end)

match.isTeamMatch = Array.any(match.opponents, function(opponent)
return opponent.type == Opponent.team end
)

local extradata = match.extradata
LuckeLucky marked this conversation as resolved.
Show resolved Hide resolved
---@cast extradata table
if match.isTeamMatch then
-- Compute submatches
match.submatches = Array.map(
CustomMatchGroupUtil.groupBySubmatch(match.games),
function(games) return CustomMatchGroupUtil.constructSubmatch(games) end
)

-- Extract submatch headers from extradata
for _, submatch in pairs(match.submatches) do
LuckeLucky marked this conversation as resolved.
Show resolved Hide resolved
submatch.header = Table.extract(extradata, 'subgroup' .. submatch.subgroup .. 'header')
end
end

return match
end

---@param game MatchGroupUtilGame
---@param matchOpponents standardOpponent[]
---@return table[]
function CustomMatchGroupUtil.computeGameOpponents(game, matchOpponents)
return Array.map(game.opponents, function (opponent, opponentIndex)
return Table.merge(opponent, {
players = Array.map(game.opponents[opponentIndex].players or {},function (player, playerIndex)
LuckeLucky marked this conversation as resolved.
Show resolved Hide resolved
if Logic.isEmpty(player) then return nil end
hjpalpha marked this conversation as resolved.
Show resolved Hide resolved
return Table.merge(matchOpponents[opponentIndex].players[playerIndex] or {}, player)
end)
})
end)
end

---Group games on the subgroup field to form submatches
---@param matchGames MatchGroupUtilGame[]
---@return MatchGroupUtilGame[][]
function CustomMatchGroupUtil.groupBySubmatch(matchGames)
-- Group games on adjacent subgroups
local previousSubgroup = nil
local currentGames = nil
local submatchGames = {}
for _, game in ipairs(matchGames) do
LuckeLucky marked this conversation as resolved.
Show resolved Hide resolved
if previousSubgroup == nil or previousSubgroup ~= game.subgroup then
currentGames = {}
table.insert(submatchGames, currentGames)
previousSubgroup = game.subgroup
end
---@cast currentGames -nil
table.insert(currentGames, game)
end
return submatchGames
end

---Constructs a submatch object whose properties are aggregated from that of its games.
---@param games MatchGroupUtilGame[]
---@return HearthstoneMatchGroupUtilSubmatch
function CustomMatchGroupUtil.constructSubmatch(games)
local opponents = Table.deepCopy(games[1].opponents)

-- Sum up scores
local scores = {}
for opponentIndex, _ in pairs(opponents) do
scores[opponentIndex] = 0
end
for _, game in pairs(games) do
LuckeLucky marked this conversation as resolved.
Show resolved Hide resolved
if game.map and String.startsWith(game.map, 'Submatch') and not game.resultType then
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

for opponentIndex, score in pairs(scores) do
scores[opponentIndex] = score + (tonumber(game.scores[opponentIndex]) or 0)
end
elseif game.winner then
scores[game.winner] = (scores[game.winner] or 0) + 1
end
end

-- Compute winner if all games have been played, skipped, or defaulted
local allPlayed = Array.all(games, function(game)
return game.winner ~= nil or game.resultType ~= nil
end)

local resultType = nil
local winner = nil
if allPlayed then
local diff = (scores[1] or 0) - (scores[2] or 0)
if diff < 0 then
winner = 2
elseif diff == 0 then
resultType = 'draw'
else
winner = 1
end
end

-- Set resultType and walkover if every game is a walkover
local walkovers = {}
local resultTypes = {}
for _, game in pairs(games) do
resultTypes[game.resultType or ''] = true
walkovers[game.walkover or ''] = true
end
local walkover
local uniqueResult = Table.uniqueKey(resultTypes)
if uniqueResult == 'default' then
resultType = 'default'
walkover = String.nilIfEmpty(Table.uniqueKey(walkovers)) or 'L'
elseif uniqueResult == 'np' then
resultType = 'np'
end

return {
games = games,
opponents = opponents,
resultType = resultType,
scores = scores,
subgroup = games[1].subgroup,
walkover = walkover,
winner = winner,
}
end

return CustomMatchGroupUtil
Loading
Loading