--[[ world2ch Umamusume PVP Race Generator by MachDiamond Version 2026/5/14 Written for Lua 5.4 Usage: The filter table is the main thing. You just remove or add whatever you want to the filter table to narrow down your results. Use the display_* variables to disable the display of conditions you do not wish to see. The display table exists to allow you to modify the output of numbers and names. Tracks is every single track in the game. Unless you intend to update the script, this is best left alone. For future expansion and personal modification: Line 27: Filter table Line 80: Display table Line 164: Track table Line 836: apairs (Accurate Pairs function) Line 867: Debug configuration output Line 931: Race sieving (creating a list of every single track that can meet the filter table) Line 985: RNG caps Line 997: Sieve scrambling (scrambles the potential options that were collected by the race sieving) Line 1020: Track course output Line 1055: Track conditions output I recommend popping this bitch into https://onecompiler.com/lua rather than downloading a Lua version. Happy races! ]] local filter = { -- Omit what you don't want, or in the case of display_*/rank_min/rank_max, set to false. Race filters are case-insensitive. ["turns"] = {"Right", "Left", "Stretch"}, -- ["distance"] = { -- The distances of the track. -- Sprint -- Standard 1200, -- Non-Standard 1000, 1150, 1300, 1400, -- Mile -- Standard 1600, -- Non-Standard 1500, 1700, 1800, -- Medium -- Standard 2000, 2400, -- Non-Standard 1900, 2100, 2200, 2300, -- Long -- Standard 3200, 3600 , -- Non-Standard 2500, 2600, 3000, 3400, }, ["material"] = {"Turf", "Dirt"}, -- Track material. ["track"] = {"None", "Inner Track", "Outer Track", "Outer → Inner Track"}, -- Track-types. ["name"] = {"Sapporo", "Hakodate", "Fukushima", "Oi", "Kokura", "Hanshin", "Kyoto", "Chukyo", "Nakayama", "Tokyo", "Niigata"}, -- Fields you want to be able to use. ["size"] = {14, 15, 16, 18}, -- Maximum track field size. ["field"] = {9, 10, 11, 12, 13, 14, 15, 16, 17, 18}, -- Selected track size. ["display_material"] = true, -- Display the material of the track. ["display_distance"] = true, -- Display the actual distance of the track. ["display_distance_short"] = true, -- Display the short-hand of the track's distance. ["display_turns"] = true, -- Display the turn of the track. ["display_track"] = true, -- Display the track-position. ["display_name"] = true, -- Display the field's name. ["display_size"] = true, -- Display the max racers available for the selected course. ["display_field"] = true, -- Display a randomly generated field size. -- These are randomly generated at the end of selecting a course. ["mood"] = {"Random", "Great", "Good", "Normal", "Bad", "Awful"}, -- The moods. ["season"] = { -- The seasons and their available weathers. ["Random"] = {"Random", "Sunny Firm", "Good Firm", "Cloudy Firm", "Cloudy Firm", "Rainy Soft", "Rainy Heavy", "Snowy Good", "Snowy Soft"}, ["Spring"] = {"Random", "Sunny Firm", "Good Firm", "Cloudy Firm", "Cloudy Firm", "Rainy Soft", "Rainy Heavy"}, ["Summer"] = {"Random", "Sunny Firm", "Good Firm", "Cloudy Firm", "Cloudy Firm", "Rainy Soft", "Rainy Heavy"}, ["Fall"] = {"Random", "Sunny Firm", "Good Firm", "Cloudy Firm", "Cloudy Firm", "Rainy Soft", "Rainy Heavy"}, ["Winter"] = {"Random", "Sunny Firm", "Good Firm", "Cloudy Firm", "Cloudy Firm", "Rainy Soft", "Rainy Heavy", "Snowy Good", "Snowy Soft"}}, ["rank_limit"] = {"None", "F", "E", "D", "C", "B", "A", "A+", "S", "S+", "SS", "SS+"}, -- Available ranks. ["rank_min"] = true, -- Enable the rank minimum choice ["rank_max"] = true, -- Enable the rank maximum choice ["display_weather"] = true, -- Display randomized weather. ["display_season"] = true, -- Display randomized season. ["display_mood"] = true, -- Display a randomized mood. } local display = { -- Affects the display. If you wanna change the units or course names, then you go here. ["distance"] = { -- Distance names -- [threshold] = "Name", [1000] = "Sprint", [1500] = "Mile", [1900] = "Medium", [2500] = "Long" }, ["unit"] = { -- Convert the metric into another display unit. ["name"] = "m", -- Display name ["conversion"] = 1, -- Meters * This -> Unit ["precision"] = 0, -- Decimal }, ["name"] = { -- Field names. -- ["name"] = "Displayed Name" ["Sapporo"] = "Sapporo", ["Hakodate"] = "Hakodate", ["Fukushima"] = "Fukushima", ["Oi"] = "Oi", ["Kokura"] = "Kokura", ["Hanshin"] = "Hanshin", ["Kyoto"] = "Kyoto", ["Chukyo"] = "Chukyo", ["Nakayama"] = "Nakayama", ["Tokyo"] = "Tokyo", ["Niigata"] = "Niigata", }, ["material"] = { -- Displayed name for the materials. ["Turf"] = "T", ["Dirt"] = "D", }, --[[ -- Alternate names and units. Japanese shakkanhou chou, and Japanese kanji names provided. ["unit"] = { ["name"] = "町", -- Display name ["conversion"] = 11/1200, -- Meters * This -> Unit ["precision"] = 1, -- Decimal }, ["name"] = { ["Sapporo"] = "札幌 (Sapporo)", ["Hakodate"] = "函館 (Hakodate)", ["Fukushima"] = "福島 (Fukushima)", ["Oi"] = "大井 (Oi)", ["Kokura"] = "小倉 (Kokura)", ["Hanshin"] = "阪神 (Hanshin)", ["Kyoto"] = "京都 (Kyoto)", ["Chukyo"] = "中京 (Chukyo)", ["Nakayama"] = "中山 (Nakayama)", ["Tokyo"] = "東京 (Tokyo)", ["Niigata"] = "新潟 (Niigata)", },]] } --[[ Example track: ["name"] = { -- Each possible combination is stored as the following: { ["turns"] = "Turn Direction", ["distance"] = distance in meters, ["material"] = "Track's Material", ["track"] = "Track Position", ["size"] = max racers, }, -- Material(abbrv) Distance(m) (Distance name) Turn Direction / Track Position MR: Max Racers -- Repeat ad infinitum until all track combinations are stored. -- Here's some examples. { ["turns"] = "Stretch", ["distance"] = 1000, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- T 1000m (Sprint) Stretch MR: 16 { ["turns"] = "Right", ["distance"] = 1700, ["material"] = "Dirt", ["track"] = "None", ["size"] = 14, }, -- D 1700m (Mile) Right MR: 14 }, Everything SHOULD be capitalized, as only the race filters are case-insensitive. The display mechanisms are NOT. ]] local tracks = { -- Warning: Sizeable glob of data! ["Sapporo"] = { { ["turns"] = "Right", ["distance"] = 2600, ["material"] = "Turf", ["track"] = "None", ["size"] = 14, }, -- T 2600m (Long) Right MR: 14 { ["turns"] = "Right", ["distance"] = 2000, ["material"] = "Turf", ["track"] = "None", ["size"] = 16, }, -- T 2000m (Medium) Right MR: 16 { ["turns"] = "Right", ["distance"] = 1800, ["material"] = "Turf", ["track"] = "None", ["size"] = 14, }, -- T 1800m (Mile) Right MR: 14 { ["turns"] = "Right", ["distance"] = 1500, ["material"] = "Turf", ["track"] = "None", ["size"] = 14, }, -- T 1500m (Mile) Right MR: 14 { ["turns"] = "Right", ["distance"] = 1200, ["material"] = "Turf", ["track"] = "None", ["size"] = 16, }, -- T 1200m (Sprint) Right MR: 14 { ["turns"] = "Right", ["distance"] = 1700, ["material"] = "Dirt", ["track"] = "None", ["size"] = 14, }, -- D 1700m (Mile) Right MR: 14 }, ["Hakodate"] = { { ["turns"] = "Right", ["distance"] = 1700, ["material"] = "Dirt", ["track"] = "None", ["size"] = 14, }, -- D 1700m (Mile) Right MR: 14 { ["turns"] = "Right", ["distance"] = 1000, ["material"] = "Turf", ["track"] = "None", ["size"] = 14, }, -- T 1000m (Sprint) Right MR: 14 { ["turns"] = "Right", ["distance"] = 1200, ["material"] = "Turf", ["track"] = "None", ["size"] = 16, }, -- T 1200m (Sprint) Right MR: 16 { ["turns"] = "Right", ["distance"] = 1800, ["material"] = "Turf", ["track"] = "None", ["size"] = 16, }, -- T 1800m (Mile) Right MR: 16 { ["turns"] = "Right", ["distance"] = 2000, ["material"] = "Turf", ["track"] = "None", ["size"] = 14, }, -- T 2000m (Medium) Right MR: 16 { ["turns"] = "Right", ["distance"] = 2600, ["material"] = "Turf", ["track"] = "None", ["size"] = 16, }, -- T 2600m (Long) Right MR: 16 }, ["Fukushima"] = { { ["turns"] = "Right", ["distance"] = 2600, ["material"] = "Turf", ["track"] = "None", ["size"] = 16, }, -- T 2600m (Long) Right MR: 16 { ["turns"] = "Right", ["distance"] = 2000, ["material"] = "Turf", ["track"] = "None", ["size"] = 16, }, -- T 2000m (Medium) Right MR: 16 { ["turns"] = "Right", ["distance"] = 1800, ["material"] = "Turf", ["track"] = "None", ["size"] = 16, }, -- T 1800m (Mile) Right MR: 16 { ["turns"] = "Right", ["distance"] = 1200, ["material"] = "Turf", ["track"] = "None", ["size"] = 14, }, -- T 1200m (Sprint) Right MR: 14 { ["turns"] = "Right", ["distance"] = 1700, ["material"] = "Dirt", ["track"] = "None", ["size"] = 15, }, -- D 1700m (Mile) Right MR: 15 { ["turns"] = "Right", ["distance"] = 1150, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 1150m (Sprint) Right MR: 16 }, ["Oi"] = { { ["turns"] = "Right", ["distance"] = 2000, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 2000m (Medium) Right MR: 16 { ["turns"] = "Right", ["distance"] = 1800, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 1800m (Mile) Right MR: 16 { ["turns"] = "Right", ["distance"] = 1200, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 1200m (Sprint) Right MR: 16 }, ["Kokura"] = { { ["turns"] = "Right", ["distance"] = 2600, ["material"] = "Turf", ["track"] = "None", ["size"] = 16, }, -- T 2600m (Long) Right MR: 16 { ["turns"] = "Right", ["distance"] = 2000, ["material"] = "Turf", ["track"] = "None", ["size"] = 18, }, -- T 2000m (Medium) Right MR: 18 { ["turns"] = "Right", ["distance"] = 1800, ["material"] = "Turf", ["track"] = "None", ["size"] = 16, }, -- T 1800m (Mile) Right MR: 16 { ["turns"] = "Right", ["distance"] = 1200, ["material"] = "Turf", ["track"] = "None", ["size"] = 18, }, -- T 1200m (Sprint) Right MR: 18 { ["turns"] = "Right", ["distance"] = 1700, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 1700m (Mile) Right MR: 16 }, ["Hanshin"] = { { ["turns"] = "Right", ["distance"] = 1400, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 1400m (Sprint) Right MR: 16 { ["turns"] = "Right", ["distance"] = 1800, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 1800m (Mile) Right MR: 16 { ["turns"] = "Right", ["distance"] = 2000, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 2000m (Medium) Right MR: 16 { ["turns"] = "Right", ["distance"] = 1200, ["material"] = "Turf", ["track"] = "Inner Track", ["size"] = 16, }, -- T 1200m (Sprint) Right / Inner MR: 16 { ["turns"] = "Right", ["distance"] = 1400, ["material"] = "Turf", ["track"] = "Inner Track", ["size"] = 18, }, -- T 1400m (Sprint) Right / Inner MR: 18 { ["turns"] = "Right", ["distance"] = 1600, ["material"] = "Turf", ["track"] = "Outer Track", ["size"] = 18, }, -- T 1600m (Mile) Right / Outer MR: 18 { ["turns"] = "Right", ["distance"] = 1800, ["material"] = "Turf", ["track"] = "Outer Track", ["size"] = 18, }, -- T 1800m (Mile) Right / Outer MR: 18 { ["turns"] = "Right", ["distance"] = 2000, ["material"] = "Turf", ["track"] = "Inner Track", ["size"] = 16, }, -- T 2000m (Medium) Right / Inner MR: 16 { ["turns"] = "Right", ["distance"] = 2200, ["material"] = "Turf", ["track"] = "Inner Track", ["size"] = 18, }, -- T 2200m (Medium) Right / Inner MR: 18 { ["turns"] = "Right", ["distance"] = 2400, ["material"] = "Turf", ["track"] = "Outer Track", ["size"] = 18, }, -- T 2400m (Medium) Right / Outer MR: 18 { ["turns"] = "Right", ["distance"] = 2600, ["material"] = "Turf", ["track"] = "Outer Track", ["size"] = 18, }, -- T 2600m (Long) Right / Outer MR: 18 { ["turns"] = "Right", ["distance"] = 3000, ["material"] = "Turf", ["track"] = "Inner Track", ["size"] = 16, }, -- T 3000m (Long) Right / Inner MR: 16 { ["turns"] = "Right", ["distance"] = 3200, ["material"] = "Turf", ["track"] = "Outer → Inner Track", ["size"] = 18, }, -- T 3200m (Medium) Right / Inner-Outer MR: 18 }, ["Kyoto"] = { { ["turns"] = "Right", ["distance"] = 1200, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 1200m (Sprint) Right MR: 16 { ["turns"] = "Right", ["distance"] = 1400, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 1400m (Sprint) Right MR: 16 { ["turns"] = "Right", ["distance"] = 1800, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 1800m (Mile) Right MR: 16 { ["turns"] = "Right", ["distance"] = 1900, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 1900m (Medium) Right MR: 16 { ["turns"] = "Right", ["distance"] = 1200, ["material"] = "Turf", ["track"] = "Inner Track", ["size"] = 18, }, -- T 1200m (Sprint) Right / Inner MR: 18 { ["turns"] = "Right", ["distance"] = 1600, ["material"] = "Turf", ["track"] = "Inner Track", ["size"] = 18, }, -- T 1600m (Mile) Right / Inner MR: 18 { ["turns"] = "Right", ["distance"] = 1400, ["material"] = "Turf", ["track"] = "Outer Track", ["size"] = 18, }, -- T 1400m (Sprint) Right / Outer MR: 18 { ["turns"] = "Right", ["distance"] = 1600, ["material"] = "Turf", ["track"] = "Outer Track", ["size"] = 18, }, -- T 1600m (Mile) Right / Outer MR: 18 { ["turns"] = "Right", ["distance"] = 1800, ["material"] = "Turf", ["track"] = "Outer Track", ["size"] = 18, }, -- T 1800m (Medium) Right / Outer MR: 18 { ["turns"] = "Right", ["distance"] = 2000, ["material"] = "Turf", ["track"] = "Inner Track", ["size"] = 18, }, -- T 2000m (Medium) Right / Inner MR: 18 { ["turns"] = "Right", ["distance"] = 2200, ["material"] = "Turf", ["track"] = "Outer Track", ["size"] = 18, }, -- T 2200m (Medium) Right / Outer MR: 18 { ["turns"] = "Right", ["distance"] = 2400, ["material"] = "Turf", ["track"] = "Outer Track", ["size"] = 18, }, -- T 2400m (Medium) Right / Outer MR: 18 { ["turns"] = "Right", ["distance"] = 3000, ["material"] = "Turf", ["track"] = "Outer Track", ["size"] = 18, }, -- T 3000m (Long) Right / Outer MR: 18 { ["turns"] = "Right", ["distance"] = 3200, ["material"] = "Turf", ["track"] = "Outer Track", ["size"] = 18, }, -- T 3200m (Long) Right / Outer MR: 18 }, ["Chukyo"] = { { ["turns"] = "Left", ["distance"] = 1600, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 1400m (Sprint) Left MR: 16 { ["turns"] = "Left", ["distance"] = 1800, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 1400m (Mile) Left MR: 16 { ["turns"] = "Left", ["distance"] = 1200, ["material"] = "Turf", ["track"] = "None", ["size"] = 18, }, -- T 1200m (Sprint) Left MR: 18 { ["turns"] = "Left", ["distance"] = 1400, ["material"] = "Turf", ["track"] = "None", ["size"] = 18, }, -- T 1400m (Sprint) Left MR: 18 { ["turns"] = "Left", ["distance"] = 1600, ["material"] = "Turf", ["track"] = "None", ["size"] = 16, }, -- T 1600m (Mile) Left MR: 16 { ["turns"] = "Left", ["distance"] = 2000, ["material"] = "Turf", ["track"] = "None", ["size"] = 18, }, -- T 2000m (Medium) Left MR: 18 { ["turns"] = "Left", ["distance"] = 2200, ["material"] = "Turf", ["track"] = "None", ["size"] = 18, }, -- T 2200m (Medium) Left MR: 18 }, ["Nakayama"] = { { ["turns"] = "Right", ["distance"] = 1200, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 1200m (Sprint) Right MR: 16 { ["turns"] = "Right", ["distance"] = 1800, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 1800m (Sprint) Right MR: 16 { ["turns"] = "Right", ["distance"] = 1200, ["material"] = "Turf", ["track"] = "Outer Track", ["size"] = 16, }, -- T 1200m (Sprint) Right / Outer MR: 16 { ["turns"] = "Right", ["distance"] = 1600, ["material"] = "Turf", ["track"] = "Outer Track", ["size"] = 16, }, -- T 1600m (Mile) Right / Outer MR: 16 { ["turns"] = "Right", ["distance"] = 1800, ["material"] = "Turf", ["track"] = "Inner Track", ["size"] = 16, }, -- T 1800m (Mile) Right / Inner MR: 16 { ["turns"] = "Right", ["distance"] = 2000, ["material"] = "Turf", ["track"] = "Inner Track", ["size"] = 18, }, -- T 2000m (Medium) Right / Inner MR: 18 { ["turns"] = "Right", ["distance"] = 2200, ["material"] = "Turf", ["track"] = "Outer Track", ["size"] = 18, }, -- T 2200m (Mile) Right / Outer MR: 18 { ["turns"] = "Right", ["distance"] = 2500, ["material"] = "Turf", ["track"] = "Inner Track", ["size"] = 16, }, -- T 2500m (Long) Right / Inner MR: 16 { ["turns"] = "Right", ["distance"] = 3600, ["material"] = "Turf", ["track"] = "Inner Track", ["size"] = 16, }, -- T 1800m (Mile) Right / Inner MR: 16 }, ["Tokyo"] = { { ["turns"] = "Left", ["distance"] = 1300, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 1300m (Sprint) Left MR: 16 { ["turns"] = "Right", ["distance"] = 1400, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 1400m (Sprint) Right / Inner MR: 16 { ["turns"] = "Left", ["distance"] = 1600, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 1600m (Mile) Left MR: 16 { ["turns"] = "Left", ["distance"] = 2100, ["material"] = "Dirt", ["track"] = "None", ["size"] = 16, }, -- D 2100m (Medium) Left MR: 16 { ["turns"] = "Left", ["distance"] = 1400, ["material"] = "Turf", ["track"] = "None", ["size"] = 18, }, -- T 1400m (Sprint) Left MR: 18 { ["turns"] = "Left", ["distance"] = 1600, ["material"] = "Turf", ["track"] = "None", ["size"] = 18, }, -- T 1600m (Mile) Left MR: 18 { ["turns"] = "Left", ["distance"] = 1800, ["material"] = "Turf", ["track"] = "None", ["size"] = 18, }, -- T 1800m (Mile) Left MR: 18 { ["turns"] = "Left", ["distance"] = 2000, ["material"] = "Turf", ["track"] = "None", ["size"] = 18, }, -- T 2000m (Medium) Left MR: 18 { ["turns"] = "Left", ["distance"] = 2300, ["material"] = "Turf", ["track"] = "None", ["size"] = 18, }, -- T 2300m (Medium) Left MR: 18 { ["turns"] = "Left", ["distance"] = 2400, ["material"] = "Turf", ["track"] = "None", ["size"] = 18, }, -- T 2400m (Medium) Left MR: 18 { ["turns"] = "Left", ["distance"] = 2500, ["material"] = "Turf", ["track"] = "None", ["size"] = 18, }, -- T 2500m (Long) Left MR: 16 { ["turns"] = "Left", ["distance"] = 3400, ["material"] = "Turf", ["track"] = "None", ["size"] = 18, }, -- T 3400m (Long) Left MR: 16 }, ["Niigata"] = { { ["turns"] = "Left", ["distance"] = 1200, ["material"] = "Dirt", ["track"] = "None", ["size"] = 15, }, -- D 1200m (Sprint) Left MR: 15 { ["turns"] = "Left", ["distance"] = 1800, ["material"] = "Dirt", ["track"] = "None", ["size"] = 15, }, -- D 1800m (Mile) Left MR: 15 { ["turns"] = "Stretch", ["distance"] = 1000, ["material"] = "Turf", ["track"] = "None", ["size"] = 18, }, -- T 1000m (Sprint) Stretch MR: 18 { ["turns"] = "Left", ["distance"] = 1200, ["material"] = "Turf", ["track"] = "Inner Track", ["size"] = 18, }, -- T 1200m (Sprint) Left / Inner MR: 18 { ["turns"] = "Left", ["distance"] = 1400, ["material"] = "Turf", ["track"] = "Inner Track", ["size"] = 18, }, -- T 1400m (Sprint) Left / Inner MR: 18 { ["turns"] = "Left", ["distance"] = 1600, ["material"] = "Turf", ["track"] = "Outer Track", ["size"] = 18, }, -- T 1600m (Mile) Left / Outer MR: 18 { ["turns"] = "Left", ["distance"] = 1800, ["material"] = "Turf", ["track"] = "Outer Track", ["size"] = 18, }, -- T 1800m (Mile) Left / Outer MR: 18 { ["turns"] = "Left", ["distance"] = 2000, ["material"] = "Turf", ["track"] = "Inner Track", ["size"] = 18, }, -- T 2000m (Medium) Left / Inner MR: 18 { ["turns"] = "Left", ["distance"] = 2000, ["material"] = "Turf", ["track"] = "Outer Track", ["size"] = 18, }, -- T 2000m (Medium) Left / Outer MR: 18 { ["turns"] = "Left", ["distance"] = 2200, ["material"] = "Turf", ["track"] = "Inner Track", ["size"] = 18, }, -- T 2200m (Medium) Left / Inner MR: 18 { ["turns"] = "Left", ["distance"] = 2400, ["material"] = "Turf", ["track"] = "Inner Track", ["size"] = 18, }, -- T 2400m (Medium) Left / Inner MR: 18 }, } local debugOut = false -- Use this to diagnose the code. -- Begin code. function apairs(tab) -- "Acccurate" pairs. Sorts number-based tables. -- print("あ") local tableI = {} local tableV = {} local numerator = 0 for i, v in pairs(tab) do local iA = tonumber(i) if (iA == nil) then error(string.format("Provided table was not a numbers-only table. Position \"%s\" invalid.", i)) end local position = 1 for iP = 1, #tableI do local iS = tableI[iP] -- print(iS, iA, iA < iS, iP) if (iA == iS) then error(stirng.format("Provided table had a collision. Position \"%s\" and %d.", i, iS)) end if (iA < iS) then break end position = iP + 1 end -- print(i, v, iA, position) table.insert(tableI, position, iA) table.insert(tableV, position, v) end -- for i, v in pairs(tableI) do print(i, v, tableV[i]) end return function() numerator = numerator + 1 -- print(numerator, tableI[numerator], tableV[numerator]) return tableI[numerator], tableV[numerator] end end -- Run a series of debug checks. if (debugOut) then print("DEBUG") -- Determine if there's any duplicate tracks. for tname, track in pairs(tracks) do for i = 1, #track do for j = i + 1, #track do local sameValues = 0 local testValues = 0 for n, v in pairs(track[i]) do testValues = testValues + 1 if (track[j][n] == v) then sameValues = sameValues + 1 end end if (sameValues == testValues) then print("Duplicate: ", tname, i, j) end end end end -- Second, let's get a list of all distances. local distances = {} for _, track in pairs(tracks) do for _, set in pairs(track) do local isUnique = true local testFor = set.distance for _, dist in ipairs(distances) do if (dist == testFor) then isUnique = false; break end end if (isUnique) then table.insert(distances, testFor) end end end for _, v in pairs(distances) do local name = "Invalid" for t, d in apairs(display.distance) do -- print(v, t) if (v <= t) then break end currentName = d end print("Distance:", v, currentName) end distances = nil -- Do it for racers! local racers = {} for _, track in pairs(tracks) do for _, set in pairs(track) do local isUnique = true local testFor = set.size for _, size in ipairs(racers) do if (size == testFor) then isUnique = false; break end end if (isUnique) then table.insert(racers, testFor) end end end for _, v in pairs(racers) do print("Size:", v) end racers = nil end -- We need to generate a list of all race courses that fit the criteria of the filter. local filtered = {} for _, v in pairs(filter.name) do -- Case-insensitive course check. local comparisonName = v:lower() for tname, track in pairs(tracks) do if (tname:lower() == comparisonName) then for _, set in pairs(track) do -- Check against the filter itself. local checkedFor = 0 local success = 0 for cname, check in pairs(set) do checkedFor = checkedFor + 1 if (type(check) == "string") then -- We need to case-insensitivize this thing. local checkCase = check:lower() for _, checkValue in pairs(filter[cname]) do local checkValueCase = checkValue:lower() if (checkValueCase == checkCase) then success = success + 1; break; end end else -- Non-string check. for _, checkValue in pairs(filter[cname]) do if (checkValue == check) then success = success + 1; break; end end end if (debugOut) then print(tname, _, cname, success, checkedFor) end end if (debugOut) then print(success == checkedFor) end if (success == checkedFor) then -- We have a match! local newTable = {} for i, v in pairs(set) do newTable[i] = v end newTable.__course = tname table.insert(filtered, newTable) end end break end end end if (debugOut) then local total = 0 for _, v in pairs(tracks) do total = total + #v end print("Found: ", #filtered, total) for _, v in pairs(filtered) do print(string.format("%s: %s %sm %s / %s MR: %s", v.__course, v.material, v.distance, v.turns, v.track, v.size)) end end if (#filtered == 0) then print("There was no match to your filter!") return -1 end -- These caps are used in the math for the randomness. seasonSize = 0 for _, _ in pairs(filter.season) do seasonSize = seasonSize + 1 end -- `math.floor(math.random() * xCap + 0.5) % (#size)+ 1` will always result in the range of 1-RNG max. local filterCap = math.ceil(10^(math.ceil(math.log(#filtered, 10)))) local seasonCap = math.ceil(10^(math.ceil(math.log(seasonSize, 10)))) local moodCap = math.ceil(10^(math.ceil(math.log(#filter.mood, 10)))) local rankCap = math.ceil(10^(math.ceil(math.log(#filter.rank_limit, 10)))) -- local fieldCap = math.ceil(10^(math.ceil(math.log(#filter.field, 10)))) if (debugOut) then print(filterCap, seasonCap, moodCap, rankCap, fieldCap) end -- Scramble the race courses, to provide an extra layer of randomness. -- Kind of busy work based on a flawed older design, but whatever, konpyutaas aru fasuto do local scrambled = {} local hasScrambled = {} for i = 1, (#filtered) do local success = nil local rngesus = nil repeat rngesus = math.floor(math.random() * filterCap + 0.5) % (#filtered) + 1 if (debugOut) then print(rngesus) end success = true for _, v in pairs(hasScrambled) do if (v == rngesus) then success = false; break end end until (success) if (debugOut) then print(i, rngesus) end table.insert(hasScrambled, rngesus) scrambled[i] = filtered[rngesus] end filtered = scrambled end -- Pick a card. Any card! local courseOutput = {} local rngesus = nil local filtered = filtered[math.floor(math.random() * filterCap + 0.5) % (#filtered) + 1] --[[ while (true) do -- Diagnosing a pesky bug. local newLocation = math.floor(math.random() * filterCap + 0.5) % (#filtered) + 1 assert(newLocation <= #filtered and newLocation > 0, string.format("What da fuck? %d", newLocation)) end]] -- Construct the track display if (filter.display_material) then table.insert(courseOutput, display.material[filtered.material] or filtered.material) end if (filter.display_distance) then if (display.unit.precision == 0) then table.insert(courseOutput, string.format("%d%s", math.floor((display.unit.conversion * filtered.distance + 0.5)), display.unit.name)) else table.insert(courseOutput, string.format(string.format("%%.%df%%s", display.unit.precision), math.floor((display.unit.conversion * filtered.distance * (10^(display.unit.precision + 1))) + 0.5) / (10^display.unit.precision), display.unit.name)) end end if (filter.display_distance_short) then -- This is the most complex of the displays. local currentName = "Invalid" for t, d in apairs(display.distance) do if (debugOut) then print(filtered.distance, d, t, currentName) end if (filtered.distance <= t) then break end currentName = d end if (filter.display_distance) then table.insert(courseOutput, string.format("(%s)", currentName)) else table.insert(courseOutput, currentName) end end if (filter.display_name) then table.insert(courseOutput, display.name[filtered.__course] or filtered.__course) end if (filter.display_turns) then table.insert(courseOutput, filtered.turns) end if (filter.display_turns and filter.display_track) then table.insert(courseOutput, "/") end -- For neatness. if (filter.display_track) then table.insert(courseOutput, filtered.track) end if (filter.display_size) then table.insert(courseOutput, string.format("MR: %d", filtered.size)) end if (#courseOutput ~= 0) then print(string.format("Selected Track: %s", table.concat(courseOutput, " "))) end courseOutput = {} -- The actual track conditions. if (filter.display_field) then local newField = {} for _, v in pairs(filter.field) do if (v <= filtered.size) then table.insert(newField, v) end end local fieldCap = math.ceil(10^(math.ceil(math.log(#newField, 10)))) rngesus = math.floor(fieldCap * math.random() + 0.5) % (#newField) + 1 if (debugOut) then print(rngesus) end table.insert(courseOutput, string.format("Field #%s", newField[rngesus])) end if (filter.display_mood) then rngesus = math.floor(moodCap * math.random() + 0.5) % (#filter.mood) + 1 table.insert(courseOutput, string.format("Mood %s", filter.mood[rngesus])) end if (filter.display_season) then rngesus = math.floor(math.random() * seasonCap + 0.5) % (seasonSize) + 1 local season = 1 local condition = {} for s, _ in pairs(filter.season) do if (season == rngesus) then season = s; break; end season = season + 1 end table.insert(condition, string.format("Season %s", season)) if(filter.display_weather) then rngesus = math.floor(math.random() * seasonCap + 0.5) % (#filter.season[season]) + 1 table.insert(condition, filter.season[season][rngesus]) end table.insert(courseOutput, table.concat(condition, ":")) elseif (filter.display_weather) then rngesus = math.floor(math.random() * seasonCap + 0.5) % (#filter.season.Random) + 1 table.insert(condition, string.format("Weather %s", filter.season.Random[rngesus])) end if (filter.rank_max and filter.rank_min) then rngesus = (math.floor(math.random() * rankCap + 0.5) % ((#filter.rank_limit) * 2) + 1) local rank = (rngesus % #filter.rank_limit) + 1 if (filter.rank_limit[rank]:lower() ~= "none") then rank = filter.rank_limit[rank] if (rngesus % 2 == 1) then table.insert(courseOutput, string.format("Rank: %s or above", rank)) else table.insert(courseOutput, string.format("Rank: %s or below", rank)) end end elseif (filter.rank_max or filter.rank_min) then rngesus = math.floor(math.random() * rankCap + 0.5) % (#filter.rank_limit) + 1 if (filter.rank_limit[rngesus]:lower() ~= "none") then if (filter.rank_max) then table.insert(courseOutput, string.format("Rank: %s or above", filter.rank_limit[rngesus])) else table.insert(courseOutput, string.format("Rank: %s or below", filter.rank_limit[rngesus])) end end end if (#courseOutput ~= 0) then print(string.format("Race Conditions: %s", table.concat(courseOutput, "; "))) end