Install Steam
login
|
language
简体中文 (Simplified Chinese)
繁體中文 (Traditional Chinese)
日本語 (Japanese)
한국어 (Korean)
ไทย (Thai)
Български (Bulgarian)
Čeština (Czech)
Dansk (Danish)
Deutsch (German)
Español - España (Spanish - Spain)
Español - Latinoamérica (Spanish - Latin America)
Ελληνικά (Greek)
Français (French)
Italiano (Italian)
Bahasa Indonesia (Indonesian)
Magyar (Hungarian)
Nederlands (Dutch)
Norsk (Norwegian)
Polski (Polish)
Português (Portuguese - Portugal)
Português - Brasil (Portuguese - Brazil)
Română (Romanian)
Русский (Russian)
Suomi (Finnish)
Svenska (Swedish)
Türkçe (Turkish)
Tiếng Việt (Vietnamese)
Українська (Ukrainian)
Report a translation problem
I'm not flaunting anything. I just said i'd record the result every time I picked up a WoF card and add it to the spreadsheet I linked earlier from Google Docs.
rip
i hate to be one in 315 people, it sucks in here
*gasp*
Let me make my case before you post the silly gambler's fallacy lecture, professor. You will save yourself some embarrassment.
There is actually a precondition you must meet before you have any chance of success, and it allows you to use the card even if these preconditions are not met. I am posting relevant code from card.lua
if self.ability.name == 'The Wheel of Fortune' or self.ability.name == 'Ectoplasm' or self.ability.name == 'Hex' then
local temp_pool = (self.ability.name == 'The Wheel of Fortune' and self.eligible_strength_jokers) or
((self.ability.name == 'Ectoplasm' or self.ability.name == 'Hex') and self.eligible_editionless_jokers) or {}
if self.ability.name == 'Ectoplasm' or self.ability.name == 'Hex' or pseudorandom('wheel_of_fortune') < G.GAME.probabilities.normal/self.ability.extra then
So right off the bat we see two things
* the eligible_strength_jokers must *EXIST* (very important in a minute)
* we need to see what G.GAME.probabilities.normal and ability.extra are
When we look for the 2nd bullet, here we have a potential race condition
if self.ability.name == 'The Wheel of Fortune' then
self.eligible_strength_jokers = EMPTY(self.eligible_strength_jokers)
for k, v in pairs(G.jokers.cards) do
if v.ability.set == 'Joker' and (not v.edition) then
table.insert(self.eligible_strength_jokers, v)
end
end
end
I'm calling that a potential race condition because
* On line 1467 we don't enter the loop unless eligible_strength_jokers is populated and contains a joker we own
* On line 1533, inside a separate loop, we find the code where eligible_strength_jokers is set
This means if you pop a wheel of fortune before this loop is entered,
if G.STATE ~= G.STATES.HAND_PLAYED and G.STATE ~= G.STATES.DRAW_TO_HAND and G.STATE ~= G.STATES.PLAY_TAROT or any_state then
*phoenix write style closeup of my face across a rushing blue background*
YOU HAVE A 0% CHANCE OF TRIGGERING THIS CARD
*audience murmers*
This is why I said "almost certainly". The first loop has a null check. That often happens as a cheap quick and easy fix for a compile error so you can keep coding, and they often get left in there. We aren't checking to see the contents of the array on that line. We're just seeing if it's set, which implies it wasn't when that loop was run at some point.
Anyway this is easy for all of you gumshoes to test. All you need to do is report back here, whether you are more likely to get Wheel of Fortune *after* popping a wheel of fortune *during gameplay* and *not* in the shop, and *before* popping another Tarot card. This would need to happen on your first draw, the tilde(~) means not.
Anyway. Regardless whether that dog hunts, let's look at the probabilities.
In game.lua
probabilities = {
normal = 1,
},
and self.ability in game.lua as well
self.ability = {
name = center.name,
effect = center.effect,
set = center.set,
mult = center.config.mult or 0,
h_mult = center.config.h_mult or 0,
h_x_mult = center.config.h_x_mult or 0,
h_dollars = center.config.h_dollars or 0,
p_dollars = center.config.p_dollars or 0,
t_mult = center.config.t_mult or 0,
t_chips = center.config.t_chips or 0,
x_mult = center.config.Xmult or 1,
h_size = center.config.h_size or 0,
d_size = center.config.d_size or 0,
extra = copy_table(center.config.extra) or nil,
extra_value = 0,
type = center.config.type or '',
order = center.order or nil,
forced_selection = self.ability and self.ability.forced_selection or nil,
perma_bonus = self.ability and self.ability.perma_bonus or 0,
}
Well that's interesting. extra is the return value for copy_table. Let's see what that is.
in functions/misc_functions
function copy_table(O)
local O_type = type(O)
local copy
if O_type == 'table' then
copy = {}
for k, v in next, O, nil do
copy[copy_table(k)] = copy_table(v)
end
setmetatable(copy, copy_table(getmetatable(O)))
else
copy = O
end
return copy
end
So yeah. This may very well be NaN (not a number).
Anyway TL;DR the code for Wheel of Fortune does, indeed, introduce a plausible scenario where it would trigger significantly less than 25% of the time, if
* a precondition is not met, and
* an internal array is not correctly set
The value of extra isn't shown in that file because it's found in the game.lua file (wheel of fortune's "extra" variable is set to 4). Probabilities.normal is a variable set to 1 by default (oops all 6 doubles it, but that's not important here). This means to generate a probability of 1 in "extra" (for not just wheel of fortune but anything), it checks whether pseudorandom (a called value in the range of 0 to 1 uniformly among all floating point numbers) is less than 1/"extra". The value generated is never NaN (or anything greater than 1), but the value it's checked against can be if "extra" were 0 (which it should never be).
My argument isn't really around the code not doing what it's supposed to. I agree that's the case.
My argument is rather with the state. Loop A depends on loop B being entered with a condition at least one time, or loop A can't be entered through wheel of fortune at all.
The 2nd argument is I agree more thin and I agree it should never be the case, I was only pointing out the possibility for completeness. Basically I just wanted the fact that copy_table *could* return null entered into the discussion.
Thought I agree the extra variable here should never be anything other than 4, given that there is very likely a state transition error with the first argument, I was forced to mention this as the same sort of thing would cause this to fail as well.
In any event, just anecdotally, I have had *dramatically* better luck getting Wheel of Fortune to pop off when I first pop it off using a WoF card I got with an Emperor midgame if I pop off WoF right away before playing my first hand. Which would be in line with the conditions required to enter the 2nd loop.
Same for your "what if extra isn't set" idea. WoF's extra is set at game initialisation, in game.lua. Just look for c_wheel_of_fortune. It's impossible for it to be anything other than 4, unless you mod the game.
Here's a little experiment for you though, since you're so convinced there must be ways for the code to break: get two Oops All 6 jokers. The odds will be 4 in 4, AKA 100%. Then find when your supposed race conditions are true making the odds 0%, or when extra isn't 4 but NaN meaning it can never trigger because a random value between 0 and 1 is never larger than NaN. It'll be easily recognisable because WoF will still Nope! despite 100% odds. Protip: you'll be wasting your time.
To SAVE you some time: _RELEASE_MODE in conf.lua set to false allows you to spawn in cards from your collection, hold tab for instructions. I've used that to debunk and verify tons of bugs for people over the past months.
From this, forgive me but you seem laughably immature. So I am going to challenge you to pull up the location of the code that enables consumeables inside use_consumeable and prove that eligible_strength_jokers not being enabled will cause it to be greyed out. I have not even looked myself yet.
I am actually going to do the same and repack the exe, because that is a lot less of a silly waste of time than
In fact that, again please forgive me, asanine experiment you proposed makes this next thing you said particularly ironic,
Since anyone competent would just hardcode a variable and repack the executable.
I'm sorry for the tone but I've learned to greet immaturity with immaturity. I'm not going to go into my accomplished career because, unlike you, I have nothing invested in impressing people on the internet.
And THAT said I would really like you to improve your basic grade school reading comprehension because,
This was never in dispute, I agreed before you posted this that it would be always 4, and again, only pointed out that it was possible for the method to return 0 simply to make sure this was added to the discussion.
EDIT : I just realized your knee-jerk lizard brain may have latched onto what I just said and shouted objection and had you furiously typing a reply, which you'll post without reading this edit anyway. That will be hilarious because "the method" here refers to "copy_table".
Now if you want to continue bleating and wasting everyone's time humiliating yourself that's fine, I can just ignore you as you've proven beyond a shadow of a doubt you have nothing worthwhile to say, until you complete the very simple challenge I issued to you.
To everyone else I'm just adding this after line 1468 like a normal person.
if temp_pool == {} then
self.ability.extra = G.GAME.probabilities.normal
local card = create_card('Joker', G.jokers, nil, 0.99, nil, nil, nil, 'wra')
card:add_to_deck()
G.jokers.emplace(card)
end
Goblin while I don't think either of us would respect me if I apologized after your desparate attempt at internet one-upmanship, I have never had a problem telling other people they were right and I was wrong.
You were right.
I was able to verify after both reviewing the code and repacking the lua - which again I only opened for the first time today and looked at for all of 10 minutes before our conversation - that the use button is indeed greyed out correctly when you have no jokers, making the state I was talking about unreachable.
The wheel of fortune is definitely a 1 in 4 chance and the bug I thought was possible is not something that can happen in ordinary play.
To give some more context, line 1533 is where eligible_strength_jokers is checked before even letting you hit the use button. So it was indeed impossible for local_pool to be empty.
Moreover, I just did a little experiment with your code: add it where you wanted it, and on the line before it add local_pool = {}. Y'know, to guarantee it would run what you added. It crashes the game - you adding a Joker joker doesn't add it to local_pool, you set the probability to 100% with your second line, and thus it tries to get a random card out of a set of no cards.
That's the danger with adding code yourself - you risk introducing new bugs. Spawning two Oopses is a foolproof test - if extra isn't 4, it wouldn't become a 100% chance with two of them. If the pool could ever not be filled, you'd be able to find a moment in the game to use WoF and not get an edition on any viable joker. But in reality, the game would have crashed in that scenario anyway as seen through the above test.
There's some irony in you demanding I do homework for you when you refused to do a test I gave you. In fact, plenty of irony throughout. But whatever, my point was never "one-upmanship" and always to just point out why your ideas of a possible cause were wrong. I already dug through this code weeks ago, and simply wanted to correct some misconceptions you had. At least you learned a little more about the codebase, here's hoping it motivates you to maybe mod the game
Look, clearly neither of is is an idiot and deserves the way we were talking to each other. With this understood then, I am not going to reply with an explanation of what a test harness or a seam is, and why people in industry write automated tests in code, and how errors in the test code can themselves be mitigated with the unit test definition, because all that is likely to be knowledge we both have. Instead I am going to say that you have your approach and skillset and I respect that.
In that context I think you're capable of figuring out not only that setting the probability to 100% was clearly intentional, but why I did it, and what was under test. What was under test in the sample was not the probability directly ;)
My real aim here - which you performed beautifully at - was to put the conversation to rest, as is evident by my earlier posts. That was not possible to accomplish without a bit of drama so all told I am glad you showed up. The conversation was being dominated by people reposting stuff about the gamblers fallacy and a lot of unhelpful information. It was very clear that there was nothing wrong with the RNG at the outset, but that was never going to be enough until someone advanced the null hypothesis and wore that hat.
Someone had to come here, say there was a bug, post a case for the bug, and have it be shot down, before this conversation could finally die.
So thank you for your comments, I am actually working on my own Unreal game and can't take on another creative project right now but knowing how ridiculously easy Balatro is to mod and edit now I may consider it :)
-Code wise, there is no bug. WoF is 1 in 4, so there's a 75% Nope! when you pop a random WoF.
-Rate is seed-dependent. As soon as you fire up a new game, it's already decided which WoF will pop, which WoF will Nope!, no matter how many times you try to reload.
-There will be seed that WoF procs more than Nope!, and vice versa. The reason many people runs into Nope! is because 75% of the time we're unlucky.
-With enough digging, each seed WILL produce an amount of WoF that procs 1 in 4 (So like a seed has 20 WoF, 5 of them will proc). The reason we can't find them all is of course econ restriction.