Module:User:Cscott/Advent Of Code 2023/Day 7 Explained

return (functionlocal builders = local function register(name, f) builders[name] = fendregister('llpeg.lpegrex', function return require end)

register('llpeg', function return require end)

register('day7', function(myrequire)--DAY 7 --local lpegrex = myrequire('llpeg.lpegrex')local l = myrequire('llpeg')

--PARSING --local patt = lpegrex.compile(nl* HandBid (nl+ HandBid)* nl* |}HandBid <--

Hand <-- Card Card Card Card CardCard <-- [AKQJT98765432j]Number <-- %d+ -> tonumbernl <-- %nlSKIP <-- []*NAME_SUFFIX <-- [_%w]+)

function parse(source) --print(inspect(source)) local ast, errlabel, pos = patt:match(source) if not ast then local lineno, colno, line = lpegrex.calcline(source, pos) local colhelp = string.rep(' ', colno-1)..'^' error('syntax error: '..lineno..':'..colno..': '..errlabel.. '\n'..line..'\n'..colhelp) end --print('Parsed with success!') --print(inspect(ast)) return astend

--PART 1 --

local JOKER = 1 -- must be lower than the "2"

local card_value = local Kind =

local split_patt = l.Ct((l.P(1) / card_value)^5)function split(hand) -- split hand into cards, map each to value return split_patt:match(hand)end

function kind1(hand) -- count duplicates local count = local max = 0 for _,v in ipairs(hand) do count[v] = (count[v] or 0) + 1 max = math.max(max, count[v]) end if max

5 then return Kind.FiveOfAKind end if max

4 then return Kind.FourOfAKind end -- count pairs local pairCount = 0 for _,cnt in pairs(count) do if cnt

2 then pairCount = pairCount + 1 end end if max

3 then if pairCount

1 then return Kind.FullHouse end return Kind.ThreeOfAKind end if pairCount

2 then return Kind.TwoPair end if pairCount

1 then return Kind.OnePair end return Kind.HighCardend

function compare_hands(a, b) if a.kind ~= b.kind then return a.kind < b.kind end -- sort by first card, then second, etc. for i=1,5 do if a.split[i] ~= b.split[i] then return a.split[i] < b.split[i] end end --print(inspect(a), inspect(b)) --error("duplicate hands") return falseend

function winnings(source, compute_kind_func) local hands = parse(source) --print(inspect(hands)) -- compute the kind for every hand for _,h in ipairs(hands) do h.split = split(h.hand) h.kind = compute_kind_func(h.split) end -- sort the hands table.sort(hands, compare_hands) -- sum the winnings! local sum = 0 for rank,h in ipairs(hands) do -- print(rank,h.hand,h.bid,h.kind) sum = sum + rank * h.bid end return sumend

--Part 2 --function kind2(hand) -- count duplicates local count = local max = 0 for _,v in ipairs(hand) do count[v] = (count[v] or 0) + 1 max = math.max(max, count[v]) end local jokers = count[JOKER] or 0 if max

5 then return Kind.FiveOfAKind end if max

4 then if jokers > 0 then return Kind.FiveOfAKind end return Kind.FourOfAKind end -- count pairs local pairCount = 0 for _,cnt in pairs(count) do if cnt

2 then pairCount = pairCount + 1 end end if max

3 then if jokers

3 then -- option 1: three jokers, two matching cards if pairCount

1 then return Kind.FiveOfAKind end -- option 2: three jokers, two non matching cards return Kind.FourOfAKind elseif jokers

2 then -- option 3: three cards, two jokers return Kind.FiveOfAKind elseif jokers

1 then -- option 4: three cards, 1 card, 1 joker return Kind.FourOfAKind elseif pairCount

1 then -- option 5: no jokers, full house return Kind.FullHouse else -- option 6: no jokers return Kind.ThreeOfAKind end end -- max <= 2 if pairCount

2 then if jokers

2 then -- two jokers, two matching cards, 1 non matching return Kind.FourOfAKind elseif jokers

1 then -- one joker, two pair return Kind.FullHouse else return Kind.TwoPair end elseif pairCount

1 then if jokers > 0 then -- one pair of jokers + one card, or one pair + 1 joker return Kind.ThreeOfAKind else return Kind.OnePair end elseif jokers > 0 then return Kind.OnePair else return Kind.HighCard endend

function part1(source) return winnings(source, kind1) -- part 1end

function part2(source) source = source:gsub("J","j") -- jokers! return winnings(source, kind2)end

--CLI start ]--local source = io.input("day7.input"):read("a")print(part1(source))print(part2(source))--[[ CLI end ]]--

return

end)

local modules = modules['table'] = require('table')modules['string'] = require('string')modules['strict'] = local function myrequire(name) if modules[name]