From 8f8a2402dcd29945098cf191c72c0950644ad13e Mon Sep 17 00:00:00 2001 From: Emik Date: Mon, 23 Feb 2026 17:01:11 +0100 Subject: [PATCH] Add challenge --- localization/en-us.lua | 2 + src/challenge.lua | 26 +++++- src/lib/funky.lua | 175 ++++++++++++++++++++++++++--------------- 3 files changed, 137 insertions(+), 66 deletions(-) diff --git a/localization/en-us.lua b/localization/en-us.lua index b67e75c..22dba0b 100644 --- a/localization/en-us.lua +++ b/localization/en-us.lua @@ -350,6 +350,7 @@ return { misc = { challenge_names = { c_Roland_Jokerful = "Jokerful", + c_Roland_Ornate = "Ornate", c_Roland_Pastries = "Sweet Pastries", -- c_Roland_Surgery = "Surgery", }, @@ -371,6 +372,7 @@ return { }, v_text = { ch_c_Roland_Jokerful = {"Only the {C:common}default Joker{} can appear in shops"}, + ch_c_Roland_Ornate = {"Anything vanilla is banned"}, ch_c_Roland_Pastries = {"All blinds, cards, and tags are of {C:gold}Bakery{} or {C:blue}Roland"}, -- ch_c_Roland_Oops_All_Tranquilizers = {"Non-showdown blinds are replaced with {C:blue}The Tranquilizer"}, -- ch_c_Roland_Linked_Rank = {"Cards of the same rank share state"}, diff --git a/src/challenge.lua b/src/challenge.lua index 75b4aa7..a8c1e40 100644 --- a/src/challenge.lua +++ b/src/challenge.lua @@ -1,7 +1,11 @@ local f, q = unpack(... or require "lib.shared") -local jokerful = {banned_cards = {}} -local pastries = {banned_cards = {}, banned_tags = {}, banned_other = {}} --- local surgery = {banned_other = {}} + +local function bans() + return {banned_cards = {}, banned_tags = {}, banned_other = {}} +end + +local jokerful, pastries, vanillas = bans(), bans(), bans() +-- local surgery = bans() local function adder(tbl) return function(v) table.insert(tbl, {id = v.key}) @@ -24,6 +28,12 @@ local function is_joker(v) return v.set == "Joker" end +local function is_vanilla(v) + return v.set and not v.mod and f(G.P_CENTERS):concat(G.P_CARDS, G.P_TAGS):any(function(c) + return c.mod and c.set == v.set + end) +end + SMODS.Challenge { key = "Jokerful", rules = {custom = {{id = "Roland_Jokerful"}}}, @@ -31,6 +41,13 @@ SMODS.Challenge { pronouns = "he_him", } +SMODS.Challenge { + key = "Ornate", + rules = {custom = {{id = "Roland_Ornate"}}}, + restrictions = vanillas, + pronouns = "any_all", +} + SMODS.Challenge { key = "Pastries", rules = {custom = {{id = "Roland_Pastries"}}}, @@ -54,6 +71,9 @@ SMODS.Challenge { q(function() f { {G.P_CENTERS, is_joker, jokerful.banned_cards}, + {G.P_TAGS, is_vanilla, vanillas.banned_tags}, + {G.P_BLINDS, is_vanilla, vanillas.banned_other}, + {G.P_CENTERS, is_vanilla, vanillas.banned_cards}, {G.P_TAGS, is_banned_from_pastry, pastries.banned_tags}, {G.P_BLINDS, is_banned_from_pastry, pastries.banned_other}, {G.P_CENTERS, is_center_banned_from_pastry, pastries.banned_cards}, diff --git a/src/lib/funky.lua b/src/lib/funky.lua index 9f148a4..c85ed51 100644 --- a/src/lib/funky.lua +++ b/src/lib/funky.lua @@ -15,6 +15,25 @@ if not f then end end +---@type F +local none + +---@generic K, V +---@param tbl table +---@param fpairs? fun(t: table): (fun(table: table, index?: K): K, V) +---@return fun(tbl: table, key: K): K, V +---@return K +---@return V +local function autopairs(tbl, fpairs) + return (fpairs or tbl[#tbl] and ipairs or pairs)(tbl) +end + +---@param any any +---@return boolean +local function is_f(any) + return type(any) == "table" and any.from == f.from and any.new == f.new +end + --- Always returns nil. ---@return nil function f.noop() @@ -74,14 +93,99 @@ function f.new(fnext) return ret end +--- Creates an enumeration. ---@generic K, V ---@param tbl table ----@param fpairs? fun(t: table): (fun(table: table, index?: K): K, V) ----@return fun(tbl: table, key: K): K, V ----@return K ----@return V -local function autopairs(tbl, fpairs) - return (fpairs or tbl[#tbl] and ipairs or pairs)(tbl) +---@param fpairs? fun(t: table): (fun(table: table, index?: K): K?, V?) +---@param step? nil +---@return F | { [K]: V } +---@overload fun(tbl: number, fpairs?: number, step?: number): F | { [number]: number } +---@overload fun(tbl: string|K): fun(table: { [string|K]: V }): V +---@overload fun(tbl: false): fun(): false +---@overload fun(tbl: true): fun(): true +---@overload fun(tbl: nil): F +function f.from(tbl, fpairs, step) + if tbl == true then + return f.tru + elseif tbl == false then + return f.fals + elseif tbl == nil then + return none + end + + local tbl_type = type(tbl) + + if tbl_type == "string" then + return f.index(tbl) + elseif tbl_type == "number" then + local ik = 0 + local start = fpairs and tbl or 1 + + local stop = not fpairs and tbl or + (type(fpairs) == "number") and fpairs or error("Invalid argument type for 'fpairs': " .. type(fpairs)) + + if step and step ~= 0 and ((step < 0) == (start < stop)) then + return none + end + + return f.new(function() + local iv = tbl + ik * (step or 1) + ik = ik + 1 + + if start > stop and iv >= stop or + start < stop and iv <= stop or + start == stop and iv == stop then + return ik, iv + end + end) + elseif tbl_type ~= "table" then + error("Invalid argument type for 'tbl': " .. type(tbl)) + end + + local next, context, k, v = autopairs(tbl, fpairs) + + return f.new(function() + k, v = next(context, k) + return k, v + end) +end + +---@generic K, V +---@param self F|{ [K]: V } +---@param ... F|{ [K]: V } +---@return F|{ [K]: V } +---@nodiscard +function f:concat(...) + local fsi = 0 + local fs = {...} + + for i = 1, #fs do + if not is_f(fs[i]) then ---@diagnostic disable-next-line: assign-type-mismatch + fs[i] = f.from(fs[i]) + end + end + + return f.new(function() + if fsi == 0 then + local k, v = self:next() + + if k ~= nil then + return k, v + end + + fsi = 1 + end + + while fsi <= #fs do + local k, v = fs[fsi]:next() + + if k ~= nil then + return k, v + end + + fsi = fsi + 1 + end + end) end ---@generic K, V, U @@ -380,61 +484,6 @@ function f:each(func) end end -local none = f.new() +none = f.new() ---- Creates an enumeration. ----@generic K, V ----@param tbl table ----@param fpairs? fun(t: table): (fun(table: table, index?: K): K?, V?) ----@param step? nil ----@return F | { [K]: V } ----@overload fun(tbl: number, fpairs?: number, step?: number): F | { [number]: number } ----@overload fun(tbl: string|K): fun(table: { [string|K]: V }): V ----@overload fun(tbl: false): fun(): false ----@overload fun(tbl: true): fun(): true ----@overload fun(tbl: nil): F -return function(tbl, fpairs, step) - if tbl == true then - return f.tru - elseif tbl == false then - return f.fals - elseif tbl == nil then - return none - end - - local tbl_type = type(tbl) - - if tbl_type == "string" then - return f.index(tbl) - elseif tbl_type == "number" then - local ik = 0 - local start = fpairs and tbl or 1 - - local stop = not fpairs and tbl or - (type(fpairs) == "number") and fpairs or error("Invalid argument type for 'fpairs': " .. type(fpairs)) - - if step and step ~= 0 and ((step < 0) == (start < stop)) then - return none - end - - return f.new(function() - local iv = tbl + ik * (step or 1) - ik = ik + 1 - - if start > stop and iv >= stop or - start < stop and iv <= stop or - start == stop and iv == stop then - return ik, iv - end - end) - elseif tbl_type ~= "table" then - error("Invalid argument type for 'tbl': " .. type(tbl)) - end - - local next, context, k, v = autopairs(tbl, fpairs) - - return f.new(function() - k, v = next(context, k) - return k, v - end) -end +return f.from