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 10 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion components/match2/commons/match_group_util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ MatchGroupUtil.types.Walkover = TypeUtil.literalUnion('l', 'ff', 'dq')
---@field map string?
---@field mapDisplayName string?
---@field mode string?
---@field opponents {players: table[]}[]
---@field opponents {status: string?, score: number?, players: table[]}[]
---@field participants table
---@field resultType ResultType?
---@field scores number[]
Expand Down
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
143 changes: 143 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,143 @@
---
-- @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 Operator = require('Module:Operator')
local Table = require('Module:Table')

local MatchGroupInputUtil = Lua.import('Module:MatchGroup/Input/Util')
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 SCORE_STATUS = 'S'

local CustomMatchGroupUtil = Table.deepCopy(MatchGroupUtil)

---@class HearthstoneMatchGroupUtilGameOpponent: GameOpponent
---@field placement number?

---@class HearthstoneMatchGroupUtilSubmatch
---@field games MatchGroupUtilGame[]
---@field opponents HearthstoneMatchGroupUtilGameOpponent[]
---@field subgroup number
---@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
)

if not match.isTeamMatch then
return match
end

-- Compute submatches
match.submatches = Array.map(
CustomMatchGroupUtil.groupBySubmatch(match.games),
function(games) return CustomMatchGroupUtil.constructSubmatch(games) end
)

local extradata = match.extradata
LuckeLucky marked this conversation as resolved.
Show resolved Hide resolved
---@cast extradata table
Array.forEach(match.submatches, function (submatch)
submatch.header = Table.extract(extradata, 'subgroup' .. submatch.subgroup .. 'header')
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)
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 = {}
Array.forEach(matchGames, function (game)
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 firstGame = games[1]
local opponents = Table.deepCopy(firstGame.opponents)
local isSubmatch = string.find(firstGame.map or '', '^[sS]ubmatch %d+$')
if isSubmatch then
games = {firstGame}
end

---@param opponent table
local getOpponentScoreAndStatus = function(opponent, opponentIndex)
local statuses = Array.unique(Array.map(games, function(game)
return game.opponents[opponentIndex].status
end))
opponent.status = #statuses == 1 and statuses[1] ~= SCORE_STATUS and statuses[1] or SCORE_STATUS
opponent.score = isSubmatch and firstGame.scores[opponentIndex] or Array.reduce(Array.map(games, function(game)
return (game.winner == opponentIndex and 1 or 0)
end), Operator.add)
end

Array.forEach(opponents, getOpponentScoreAndStatus)

local allPlayed = Array.all(games, function (game) return game.winner ~= nil end)
local winner = allPlayed and MatchGroupInputUtil.getWinner('', nil, opponents) or nil
Array.forEach(opponents, function(opponent, opponentIndex)
opponent.placement = MatchGroupInputUtil.placementFromWinner('', winner, opponentIndex)
end)

return {
games = games,
opponents = opponents,
subgroup = firstGame.subgroup,
winner = winner,
}
end

return CustomMatchGroupUtil
Loading
Loading