Posts for Bobo_the_King


Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
CasualPokePlayer wrote:
GB disassembler takes in a 16 bit address internally as disassembly is expected to use System Bus for addressing (and using other domains typically makes no sense wrt addressing). I'm not quite sure if this is possible to fix per se, as disassembly would be expected to roll over at 0xFFFF -> 0x0000 for System Bus and addressing otherwise within the disassembly would be completely wrong unless System Bus is used (maybe disassembler allowing for multiple domains could be considered a misfeature here?).
Thanks for the prompt reply! I understand where you're coming from, but I'm investigating hypothetical bank switching. If I know from the assembly elsewhere that I'm in bank 7 and especially if this is reset between VBLANKs, I have no clean way of accessing that portion of the ROM. I'm reluctant to build my own instruction disassembler when BizHawk has a perfectly good one built in.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
emu.disassemble() does not properly capture high ROM addresses, at least in Game Boy games (check other systems too). It looks like it folds your query down to the range $0000 - $FFFF. Here's two commands I made directly from the Lua console and their outputs:
memory.getmemorydomainsize("ROM")
--> 131072
emu.disassemble(0x1C000, "ROM")
--> "disasm": "C000:  5B        ld e, e"        "length": "1"
Decimal value 131072 corresponds to hex value 0x20000, so BizHawk knows $1C000 is in range but ignores the "1" and just happily queries address $C000.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Thy Dungeonman when?
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
No female Ranma = no vote. /s
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
This post was inspired by this week's Riddler Classic but is not strictly about that problem. Instead, it's what appears to be a counterexample to the (negative) solution to Hilbert's third problem. I'm hoping someone can help me figure out where my reasoning is presumably wrong. I'm also going to avoid drawing any figures, but I can produce them if you think it would be helpful. As part of attempting the Riddler Classic, I broke things down into a four dimensional region in which 0<w<x<y<z<1. (You can sort of see why I would want to do this, with w, x, y, and z being the four teams in increasing order of quality.) Like your typical mere mortal, I'm no good at visualizing things in four dimensions so I decided to draw the corresponding problem for three dimensions: 0<x<y<z<1. This volume is a tetrahedron embedded within the unit cube. With a little reasoning, you can determine the edges of this tetrahedron:
  • x=y=0, z free
  • y=z=1, x free
  • x=0, z=1, y free
  • x=0, y=z
  • z=1, x=y
  • x=y=z
Upon inspection, you can see that each of these edges is indeed on the boundary of 0<x<y<z<1. There are six of them, so it is a tetrahedron as well. Well here's the insight: My choice of 0<x<y<z<1 is arbitrary and I can permute the order of these variables in six different ways (3!=6). Every point within the cube must be represented by one of these six permutations and by symmetry, each such tetrahedron must have a volume of one-sixth of the cube. Since it's a unit cube, the volume of any such tetrahedron is 1/6. We can proceed from here in two equivalent ways. First, on its face, I've determined the volume of a certain tetrahedron from its base (which we know to have area 1/2) and it tapers uniformly to a point from that base to produce total volume 1/3*1/2 = 1/6. Alternatively (what I did), we can consider a particular second permutation: 0<y<x<z<1. I was able to show that this tetrahedron lies adjacent to the first tetrahedron such that the entire z=1 face of the cube is represented by both together and they taper to a point at x=y=z=0. This is thus a pyramid whose base is area 1 and height is 1 for a total volume of 1/6 + 1/6 (the two tetrahedra) = 1/3. This bugs me. Hilbert's third problem was motivated by failed attempts by Gauss to produce a cut-and-glue formula for the volume of a pyramid and I seem to have done essentially that. It was later shown, using mathematics beyond my pay grade (Dehn invariants), that such a solution does not in general exist. Furthermore, once we have the volume of one pyramid-like solid, we can apply uniform scaling and/or Cavalieri's principle to show that the volume of any pyramid-like solid must also be 1/3 * base * height. I've proved this formula (Maybe? I think?) without using a limiting process or calculus, which Wikipedia seems to indicate is strictly necessary. So what gives? My reasoning seems pretty ironclad, so my guess is that I've just misinterpreted the thrust of Hilbert's third problem. Wikipedia also seems to suggest that Cavalieri's principle arises from calculus but... that seems like some pretty weak calculus. The essence of Cavalieri's principle can be taught to a young student, years before they do any calculus whatsoever.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
FractalFusion wrote:
Here's a problem I've been thinking about: ---- You start with a positive number N>1 and want to get down to 1 in a number of moves. Each move consists of going from the current number to a smaller positive number random based on uniform randomness. That is, from k, randomly go to a number 1, 2, ... , k-1 , each with equal probability. Keep doing this until you reach the number 1. For example, starting from 8, a sample three-move sequence is 8→5→2→1: Starting with 8, go from 8→5 (5 is chosen with probability 1/7), then from 5→2 (2 is chosen with probability 1/4), then from 2→1 (1 is chosen with probability 1). Starting from N, what is the expected number of moves to reach the number 1?
Is this only coincidentally this week's Riddler Express? I wrote up a solution up to N=8 while I was bored over dinner, but forgot to take the slip of paper with me and haven't bothered to reproduce the results I found. Anyway, regarding my previous post over Twitch wagering, I plotted a random walk of Twitch wagers. The horizontal axis sorts the wagers from most favorable odds to least favorable (although least to most would work just as well). For the vertical axis, take a step either up or down weighted according to the likelihood of success. For example, if viewers give a wager 7:3 odds in favor of success, then if the streamer is successful, take a step of 30 units upward, whereas if they fail, take a step of 70 units downward. By this construction, if wagers accurately reflect probability, the distribution should remain centered around y=0. Systematic deviation from this axis indicates an opportunity to earn channel points according to others' over-/underestimates. (Technically, of course, it is not the value of the function but its "derivative" that matters, but in the limit as the number of wagers plotted approaches infinity, we expect a non-differentiable function. We can still eyeball systematic trends as we would looking at stock prices, though. I plotted a cubic spline through it to help.) The result: For the streamer whose wagers I've plotted, I've tracked about 35 so far and there appear to be inflection points roughly around 100% odds and 60% odds, a maximum near 80% odds, and a dearth of data in the 0% to 30% range. This means that for strong odds, viewers are actually overly doubtful and it is best to bet in the streamer's favor. For longer odds, it's then better to doubt (although the trend turns around again past 30%, but this is more noise than signal). Near 80 percent chance of success, it is best to not wager or, if I do wager, I can expect to break even.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
I wish to "cheat" at Twitch predictions. By "cheat", I actually just mean to look at statistics on how users wager and determine when it would be ideal to wager. For those not familiar with it, on some channels when the streamer so cares, they can "open a prediction" in which users wager channel points. For the sake of this discussion, we'll assume that the predictions are binary. All channel points are distributed to the winners of the prediction according to the number of channel points wagered. For example, a prediction might be, "Will [streamer] beat the next level in under two minutes?" After five minutes of open wagering, let's suppose 300,000 channel points are wagered on "Yes" and 100,000 channel points are wagered on "No", giving 3:1 odds overall. If I were to wager 60 points on "Yes", I stand to lose all 60 points if I'm wrong but gross 80 points (net 20) if I'm right. If I were to wager 60 points on "No", I stand to lose 60 points if I'm wrong but gross 240 points (net 180) if I'm right. If viewers a) had perfect information and b) were perfectly rational, the efficient-market hypothesis states that it would be impossible for me or anyone to gain channel points through wagering. But I suspect (and already have some evidence in my favor) that viewers are a bit optimistic, being too eager to bet points on the streamer's success just because they want to show their belief in them. The question is whether I can quantify this bias and understand its limitations to produce a betting strategy. Now, I already have a method for doing this and it's produced some interesting results, albeit with a small sample size, but I'm curious what the canonical method of analysis is. This is surely a well-studied problem in statistics and it's a bit of a weak point in my education.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
I'm going to admit I haven't read the last few posts because it's late and I'm mostly bored, but I see that they're rather long and involved, using a lot of programming and invoking Mobius transformations among other things. Which is all fine, because there's lots of insight and additional value to be earned from diving deeper into a problem such as this. But I for one found this week's Riddler Classic to be kind of trivial. I usually at least put pen to paper, but this week I just kind of ran through the math while I was in the shower. Roughly speaking, we work backward and wish to avoid situations where our two numbers drift away from each other, such that (as is eventually inevitable) m>n (or n>m... I forget how we're choosing to label them). Well, relaxing the integer condition, we can make this series infinite by setting n=phi*m, where phi is the golden ratio. Likewise, any ansatz not such that n=phi*m will eventually drift away and terminate. Thus, we expect that the optimal solution will be the integer closest to n=phi*m or possibly second-closest. So for the "true" puzzle, we have 81/phi = 50.06 and so we expect our next lowest number to be 50 (or possibly 51) and we can work backwards from there. For the extra credit, we get 179/phi = 110.63 and our first guess should therefore be 111 or perhaps 110. (I suspect it's "extra credit" because both solutions need to be checked and they are very close, but that doesn't require that much effort.) Of course, all this is sort of "intuitive" and would need to be fleshed out into a full proof, maybe by quantifying the "drift" away from the golden ratio as the series progresses backwards. However, I would be rather shocked to find out I am a) wrong or worse, b) egregiously wrong, such that my solution is bested by another series that does many terms better. I suspect much of this is outlined somewhere in the previous posts. I'll try to dive back into them tomorrow.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
EZGames69 wrote:
How the heck did they get literally every fact about us wrong?
There's a trope for that! On topic because I feel guilty only posting a joke to this old thread, I guess I'll be my usual contrarian self and say I feel lukewarm to positive toward torrents. I haven't used them with this site myself, but I like the option and they represent one component of a vision of the internet that values free and open exchange of information. Tools and resources like BizHawk, archive.org, Blender, Photopea, Lua, and Wikipedia among countless others put powerful (albeit often imperfect) resources at your fingertips with minimal to no advertisements because they are labors of love. I still use YouTube extensively as it remains the gold standard of video hosting websites, but their platform suuuuuuucks and is rapidly getting worse. So my simple feelings toward the (since resolved) question at hand are that archive.org is a great alternative to YouTube in the absence of torrents, but should archive.org go belly-up for whatever reason, expect me to come back here and pound on the table, demanding that we reintegrate torrents. Also, regarding torrents's sketchiness, as was stated elsewhere, our whole hobby is adjacent to piracy and it is laughable to me that torrents would sully our reputation. People who are convinced that this website is about distributing ROMs or so much as encouraging users to do so are going to see that regardless of whether torrents are associated with the site. The fact that torrents are associated with piracy should be a minimal factor in any decision whether to host them on the site.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
FractalFusion wrote:
Actually I wasn't sure about your intuitive answer since it wasn't so intuitive to me. But it works out somehow! (at least as an approximation)
Thinking out loud, I'm wondering about your original characterization of the problem as geometric. Let's scrap the idea of inefficiency, which is a little harder to work with geometrically, and just assume any light lost is due to polarization. The inefficiency is modeled as a polarizer set to 8.11 degrees relative to the previous polarizer, allowing 99 percent of the light to pass through (again, ignoring Malus's law). The new problem is to put together these pairs of polarizers such that the the maximum amount of light goes through subject to the constraint that the angles between them sum to 90 degrees. Assuming they're equally spaced, we're actually dividing up 180 degrees with 2n polarizers, agreeing with my solution. (Actually, now I'm wondering why this doesn't involve logarithms anymore. I must have made an approximation somewhere along the way...) On further thought, let's try to use the same strategy to get rid of those extra angles. The arccosine of 0.995 is 5.73 degrees, so let's have one polarizer whose angle we set followed by another polarizer that's back 5.73 degrees, followed by a third polarizer ahead the same 5.73 degrees, each with 100% efficiency. Now we have one object (three polarizers) that polarize light along their orientation while allowing 99% of the light to pass through. (Yes, I'm aware that 0.995^2 is not exactly 0.99. It's very close, and the actual angle for the second and third polarizers is 5.74 degrees.) We want to divide up 90 degrees with these objects, maximizing the amount of light passing through. I'm sort of visualizing this as three right triangles joined together as one sheet of paper folded back over itself twice to make a right triangle with one loose corner corresponding to the last polarizer. We can fill up our 90 degrees with these objects, then "unfold" the paper (physically corresponding to having the second and third polarizers both set forward 5.73 degrees) and ask what angle maximizes the length of the final edge of the paper. I don't see how this intuitively arrives at 8.11 degrees since the only angle I'm explicitly working with is now 5.73 degrees, but I must be on the right track if my reasoning is sound. I dunno. I'm often tempted to say that there are no coincidences in math and that every elegant answer has an elegant proof. Godel's incompleteness theorems tell me this isn't true, but it's still hard to assume otherwise and I find that in practical applications, it tends to be true more often than not.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
p4wn3r wrote:
This one looks like a simple optimization for me. Suppose you want to do it using n steps. You want to maximize a product of cosines whose argument sums to a given number. To do this you can apply Jensen's inequality for the function log(cos(x)). Since it's concave for 0 < x < pi/2, we find that cos(x/n)^n >= prod_n cos(x_n), when the sum of all x_n is x, and equality happens when each x_n = x/n. Essentially, what all this means is that to maximize the product of cosines given that all angles sum to some fixed amount, you must choose the arguments of the cosines to divide the interval evenly. So essentially you want to find the maximum of a^n*cos(pi/2n)^n, for a = 0,99. This sequence has a maximum because it's decreasing for large n.
Despite being a physics problem, I actually didn't bother to do this week's Riddler Classic. It seemed too "easy" to me (either literally easy or at least too straightforward to provide any interesting results). What I assumed was that with each polarizer, some amount of light is lost to inefficiency and some amount is lost to the polarization itself. It stands to reason (I think...) that the number of polarizers is optimized when these two losses are equal. More polarizers means more loss to inefficiency, fewer polarizers means more loss to polarization. Thus, we expect cos(theta) = 0.99. (By the way, light intensity is proportional to the cosine squared, but whatever. Don't learn your physics from mathematicians.) I get cos-1(0.99) = 8.11 degrees. That's 90/8.11 = 11.098 polarizers, which we can round off to 11. I guess you'd want to check 12 as a possible solution, but that would require the function to have a very large third or higher derivative, making it asymmetric. Is this reasoning sound? Does it match your solution? We differentiate [a*cos(pi/2n)]n with respect to n to obtain [a*cos(pi/2n)]n*[ln[a*cos(pi/2n)] + pi/2n*tan(pi/2n)]. (To my great shame, I just had Wolfram Alpha take the derivative, although I see now I should have taken the log of the initial function and differentiated implicitly.) The first term is never zero and we're therefore left with ln[a*cos(pi/2n)] + pi/2n*tan(pi/2n) = 0. I'm sort of stuck here. We can assume n is large, leading to a few simplifications, but more importantly, I'm not able to massage this into a form reducing to a = cos(pi/2n), which would match my guess. Graphing the original function shows that there is indeed a maximum at n = 11.135, a bit higher than my own answer of 11.098 no doubt due to whatever approximation leads to this result, but they're clearly in agreement with each other. So having more or less given up on the problem at this point, I'm kicking this question back to everyone else: Why does my intuitive answer (light lost to inefficiency equals light lost to polarization) closely match the exact answer (a transcendental equation based on the derivative of the function)? As is often the case, I suspect I'm overlooking something obvious here.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
RGL wrote:
Well, I must be doing something wrong then. I tested several games and simply get no input at all. Open a game - open Lua Console - load mouse2move script ...anything else I need to do? I can see on the Debugscreen that it produces inputs but no game receieves any. "Display Input" doesnt show anything. TAStudio doesnt record any inputs either.
Hrmm... that doesn't match my observations. I tested it with a Game Boy game as well as a GBA game and not only does it accept input, it also displays it on the screen with "Display Input". One thing to keep in mind is that your mouse movements are bound to the screen. If the mouse is already at the right edge of the screen, attempting to move further right will not work. This is a limitation of how mouse coordinates are read using Lua and I don't see any workaround; even if the script were working, it would require you to move fast in one direction and then slowly move back toward the center. Actually, I take it back. As long as you aren't doing anything that requires one-to-one mapping between the mouse and cursor, it shouldn't be especially difficult to program it such that your mouse position indicates the cursor's destination and inputs are automated such that you wait for it to catch up. It's not ideal, but it's probably much closer to what you had in mind. So assuming you're still interested, could you give me the following information for the game(s) of your choice? (Still Revolution X?)
  • The RAM addresses for the x and y coordinates of the cursor. These should be easy to find with RAM Search.
  • The minimum and maximum allowable values for these x and y coordinates. Again, use RAM Search. I'll assume x and y increase left to right and top to bottom, as is conventional, but be sure to let me know if that's not the case.
  • The screen resolution of the system you're using. You can find this with gui.drawPixel or some other function.
  • Which button do you want to represent "fire" (or "action")?
If you can give me that information, I should be able to write up a new script that gets the job done and in much fewer lines to boot. Edit: Also, it just occurred to me that the issue you're seeing maybe has something to do with input being encoded for your emulator as "left" and "right" instead of "Left" and "Right", as are used in my script. I thought that BizHawk had made those inputs uniform across all emulators, but it was a problem in the past and I may have incorrectly assumed it had been standardized. You might be able to make those edits to the script yourself. Try inserting a line of print(joypad.get()) somewhere in the main loop at the bottom of the script to see what keys the emulator accepts as input.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Default Key Bindings Key bindings are defined at line 362 of my script:
  • Left bracket/right bracket: Wider/narrower RAM display
  • Numpad +/-: Taller/shorter RAM display
  • PgDn/PgUp: Next/previous "block" of RAM
  • H: Cycle through palette types (prime --> random --> monochrome --> prime)
  • J: Cycle through RAM value modes (unsigned --> signed --> hex --> unsigned)
  • K: Toggle display cursor
  • M/N: Use next/previous palette ("prime" mode only, basically uses the next/previous prime number)
  • Keypad 1-9: Display RAM at the corresponding position on the screen
Suggested Use I've had a lot of fun just using the script to "explore" the RAM. Just play the game and see if you notice anything interesting going on. But if you want to use it more systematically, start by considering just what you're looking for. In the case of Mario jumping, I can look at the RAM for pixels that change only when Mario jumps. Switching to monochrome helps because I can then look for pixels that dim or brighten as Mario follows his trajectory. The prime and random palettes are best for detecting any amount of change while monochrome is great for finding RAM values that change continuously. A quick note on palettes: "Prime" is based on producing a hash of the form p^b mod 256, where p is a prime number and b is the value of the byte in question. This means we can move backward and forward through different palettes of this type. Find one you like and make it your default value! On the other hand, "random" is exactly that-- random (except for value 0, which is always black). If you find a random palette you like, don't cycle through to monochrome or you'll never see that palette again. Possible Improvements and Open Questions I'm content with where this script is in its development, but there are a few improvements I could make. I considered adding a "blackout" feature that allows users to exclude RAM addresses that they already know they aren't interested in. This would be useful because gui.drawPixel is computationally expensive (when you run it 4,000 times per second) and the program would run faster if we didn't bother rendering those pixels. I only allow the width and height of our box to change by multiples of two. I don't care to program in other increments, but it wouldn't be all that difficult either. As such, if anyone wants that feature, I'll throw it in. Want to investigate words or larger domains? Or individual bits? Too bad! I'm not sure how I would implement those features at this time and I dread having to program in support for big and little endian. Of course, if there's overwhelming demand for it, I'll see what I can do. Finally, open questions I need help with. As of right now, the script is tailored to NES and Game Boy games. What I need to make my script more robust are a) valid address ranges for other systems and b) screen dimensions for other systems. If you can pass that information on to me, I'll transfer it straight into my script immediately. And of course, if you have any other thoughts or want me to add any features, let me know! Edit: Minor change to my script to fix an off-by-one error that prevented paging forward when on the penultimate page.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
I think you guys are going to really like this one. Get the script here! Introduction We're visual creatures and trying to page through BizHawk's RAMSearch feature to find what we're looking for is time-consuming and can be difficult. Inspiration struck me suddenly to just display many RAM values onscreen, each as a single pixel. As it so happens, someone else had the same idea five years ago, but if I may toot my own horn, my script is better (albeit surely not half as elegantly written). Then again, knowing you guys, I wouldn't be surprised if someone else here has already produced a script that does the same thing. I have a few open questions that you all can help me out with, so be sure to stick around for those at the end of this post. Demonstration What can you expect to get out of this script? I put together a short demonstration below to show off its features: Link to video (Ermm... I made it as a gif but I guess we can't embed them after they're converted to mp4 files.) So what can my script do?
  • Adjust the width and height of the RAM to be displayed, displaying more or less RAM.
  • Display a cursor over the current mouse position if you want. (I actually included that feature solely for the demo, but it is a little handy to have.)
  • Mouse over RAM pixels to find their corresponding addresses and values.
  • Change our palette to be based on a "prime hash", random, or monochrome.
  • Display RAM values as unsigned, signed, or hex.
  • Page through starting addresses for our RAM.
  • Move the RAM display around the screen so it doesn't obscure anything we might be interested in.
In the demo video, I "discover" the address(es) for Mario's y position. (Is our character limit seriously this short?)
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Arcorann wrote:
"find a lower bound on the number of pollsters required".
One. I win!
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
In addition to the known issues regarding latest posts, I prefer the old style that listed them as topics rather than posts so I could skip past posts I had no interest in. Could this be made into some kind of menu option? Or the default for "posts since last visit"? Or is it already there somewhere? Edit: Oh yeah, and there's some bug with YouTube embedding. When I'm viewing latest posts (I haven't checked elsewhere), it seems to sometimes embed whatever video is furthest down the page, or something like that. For example, currently among the latest posts is a WIP for Strider and a WIP for Prince of Persia. Under the glitch, the Strider video would be embedded in both posts. I say would be because as of right now TASVideos is behaving itself and both videos are correct. Maybe it's been fixed or maybe it has something to do with how the videos are embedded. Whatever it may be, I thought I should bring it up regardless. Edit 2: Caught an example of the YouTube embedding glitch: Edit 3: After editing my post and refreshing Latest Posts, this instance of the glitch went away. I'm not sure whether refreshing the page or my edit had anything to do with it. Also, I'm using PageSize=30 to view the 30 most recent posts. Maybe that has something to do with it.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
FractalFusion wrote:
I assume your actual question is why is the side HC the harmonic mean HM.
Not what I was after, although the geometry you point out is interesting. I'm wondering whether there's some special way to use the diagram I posted to construct two right triangles adjoined by their hypotenuse like so: This seems especially difficult because radius r and line HC (the HM) don't appear to "communicate" with each other on the figure, nor do lines GC (the GM) and OH (AM minus HM). But maybe I'm missing something obvious.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Lua feature request (assuming it isn't lurking somewhere in the functions already): gui.hold(). This function (no arguments) would save all user-drawn gui pixels for a quick redraw on a subsequent frame. The useless context that made me think of it is my Ising model project, but I imagine it would have occasional practical uses too. One might draw a HUD onscreen and overlay the gauges with lines and fills that indicate the player state. The closest thing we have is gui.drawImage. Although I haven't really tested that function's use, I'm guessing it's computationally expensive to frequently take a screenshot and load that file. I'm not even sure if gui.drawImage supports an alpha channel.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Wikipedia has this elegant image "proving" that max(a,b) > QM > AM > GM > HM > min(a,b), where the middle quantities are the quadratic, arithmetic, geometric, and harmonic means of a and b: (Of course, we can imagine max(a,b) as the "infinity-mean" and min(a,b) as the "-infinity-mean", naturally extending these definitions. Also, this isn't really a proof, but it does very quickly guide a viewer toward a proof.) My question concerns the side HG, which is not labeled as any mean. We have two formulas for HG, evident from the figure: HG^2 = r^2 - (AM-HM)^2 HG^2 = GM^2 - HM^2 Setting these two equal to each other yields the relation: r^2 - (AM-HM)^2 = GM^2 - HM^2 which can be used as the last of seven equations relating the quantities. This is good enough on its own and requires no further explanation, but we can add those negative terms to both sides to make the equation more... "hypotenuse-like" (?): r^2 + HM^2 = GM^2 + (AM-HM)^2 What I'm curious about, which may be very easy or very difficult (I'm not sure), is whether this new equation can be represented geometrically. Is there some way to swing these sides around on this figure such that the equation above is what is represented? It may be a silly question and may reveal how lazy I am, as it might be a simple step. I just don't see it immediately and don't care to rigorously investigate it. Edit: Bonus points if you can provide a geometric justification for the version where we cancel HM^2 from both sides: r^2 + 2*AM*HM = GM^2 + AM^2
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Photopea, mainly because I haven't seen any other mention of it in this thread. It's a browser-based free PhotoShop alternative, probably about 90% as good as PhotoShop and always improving.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
RGL wrote:
sorry if I am just too stupid. I can see how it works in the Debug mode and also think I get how all values work.. but, the game doesnt get any inputs. Do I have to check something first so that a Lua script is allowed to make inputs or something like that? Ive never used any script before :-/
You're not the only person who forgot to check this thread. I tested the script out in a game and it worked fine for me. Start by setting the sensitivity to the highest levels and moving the mouse vigorously to produce a constant input. If you get nothing out of it, then there's some problem with the script. Otherwise, you'll also want to check the game. Frame advance and alternate inputs of right and nil, which is commonly produced by my script. If the game does nothing, then it requires inputs in pairs, which I'm not really prepared to handle, especially since it will naturally lengthen the vector and make input laggier. If the crosshairs still move, then it again points to a problem in my script. Edit: You might also check whether the script works on other games. Sometimes there's problems with specific emulators.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Samsara is one of the community's finest members and her promotion is a strong step in the right direction for the site. Congratulations!
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Thank you for your additional thoughts, Warepire. Much of what you've written seems to overlap nicely with TASing-- working with different hardware systems, different levels of challenges, and days where you're completely stumped. I love this hobby, but it doesn't strike me as the right career path for me. Maybe it will lead me in a new direction, though. I'm still slogging through the Programming in Lua guide and although much of it is going over my head (section 8.2 on "C Packages"?), I do feel like I'm opening new doors to my understanding. I have a couple of small updates to my projects. The more interesting one comes from my Ising model. I realized that gui.drawPixel is computationally expensive and even though I cut down on its use by drawing only the less common color against a backdrop of the more common color, I still needed to go about it more efficiently. I came up with two solutions: draw line by line or block by block. Line by line is easier to implement, scanning pixels horizontally until it finds a change in color, then drawing a line knocking out a whole bunch of pixels at once. The alternative, block by block, subdivides the screen into a grid of 16x16 blocks of size 15x10, then finding the dominant color (the magnetization) within each block and drawing the pixels within line by line. This is apparently a sort of crude version of what is known as the "quadtree" graphics algorithm. The block by block algorithm has the advantage of using the dominant color of each region, which is nice because thanks to the structure at all scales feature, we expect large regions of the non-dominant color. Its disadvantages include being more difficult to program and "wasting" truncated line by line executions when, say, a black line needs to be drawn at the end of one block and it continues into the next block, necessitating two executions of gui.drawLine. (By the way, I discovered along the way that gui.drawLine does not accept single pixel inputs, such as gui.drawLine(30,40,30,40,"black"). As such, I had to cram both gui.drawLine and gui.drawPixel into one function I call line.) So which algorithm wins? They're pretty closely matched, but line by line is consistently more efficient. It speeds up the whole program by a factor of two to three, so I am now able to achieve a nice smooth 30 fps. It used to take a few hours to get the field to "equilibrate" and now it takes roughly an hour, maybe a bit less. Below is a gif of the program's output set to 30 fps, matching what I see when I run it on my computer: Here's a link to the updated program. To cycle through the draw styles, press the D key. I've left the third draw style empty, but if you'd like to compare it with pixel by pixel, you can uncomment line 287. You can also display the magnetization and temperature by pressing space or enter, respectively. The other minor update I have is to my fake words project. I had a "duhhhhhh!" moment and realized my method for adding new syllables was kind of ridiculous. The way they should be added, as far as my best guess, is to construct the network of syllables and add new syllables according to the product of their weights and total Huffman scores, the total number of bits used on those syllables. Unfortunately, I found editing my script rather confusing and I had to suppress a couple of errors so when I ran it for a few hours it only sort of worked. Some of the syllables it picked up were quite sensible: "abilitie", "ucle", "ously_", "ograph_", and "ologies_", for example. But the vast majority of the surviving syllables are just two characters in length, with only 56 (plus the original 27) being used at all, far too few to make me think that the program is working properly. Tons of other good syllables ("izations_", "ifying_", "super", "olog", "edly_", "ability_", "fulness", "ish_", etc.) are in my spreadsheet, but for some reason they have negative savings or zero instances. I've decided much of the program needs a rewrite. I'm planning on implementing an object oriented paradigm of data structures, which should be extremely well-suited to the directed graphs and Huffman trees involved in the project. If it works as well as I expect, I should be able to cut down significantly on the length of the program and make it much easier to debug. This is in addition to two or three other object oriented projects I have planned. Edit: And I just now noticed that most individual letters pick up only ~200 instances, with some letters picking up 0. Yeah, this is broken. Anyway, happy new year! I'm going to keep plugging away at these projects and I swear that I if I manage to follow through on them, you'll see some interesting results.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
New post because I don't want to keep updating my old one and my new version of the script is relatively inelegant. But here's y-axis support: Download Mouse2Move.lua
Language: lua

-- The sign of a scalar. local function sign(x) return (x>0) and 1 or ((x<0) and -1 or 0) end -- Sums over a vector. local function sum(v) local total=0 for i=1,#v do total=total+v[i] end return total end -- Did we just press this key? local function justpressed(keys, last, button) return keys[button] and not(last[button]) end -- Inner product of two vectors. If not the same length, just uses the shorter of the two. To capture the most recent values, multiplies only the last elements. local function inner(v1, v2) local prod = {} for i=0,math.min(#v1, #v2)-1 do table.insert(prod, v1[#v1-i]*v2[#v2-i]) end return sum(prod) end -- Updates the mouse's history. local function updatemouse(history, axis) local curr=input.getmouse() table.remove(history, 1) table.insert(history, curr[axis]) return history end -- Returns the difference between adjacent x values, effectively the mouse velocity. local function getdeltas(history) local delta_x = {} for i=1,#history-1 do table.insert(delta_x, history[i+1]-history[i]) end return delta_x end -- Returns a sort of weighted average of the mouse's movement based on the window function. local function windowavg(v, window) local denom = sum(window) local num = inner(v, window) return num/denom end -- Makes a window function. Exponential with time. local function makewindow(length, lambda) local window={} for i=1,length do table.insert(window, math.exp(lambda*(i-length))) end return window end -- Updates the joypad input based on the threshold and its history. Also updates and returns the history. local function gameinput(threshold, mouseavg, joyhist, joyavg, buttons) local curr=false if math.abs(mouseavg)>threshold*(1+math.abs(joyavg)) then -- "1+math.abs(joyavg)" is just a guess. It maybe needs to be refined. curr=true end local lorr = sign(mouseavg) if curr then joypad.set({[buttons[lorr]]=curr}) end table.remove(joyhist, 1) table.insert(joyhist, curr and lorr or 0) return joyhist end -- Adjusts a single parameter according to whether we press some keys. local function adjustparam(param, keys, last, downkey, upkey, mini, maxi, interval, eps) eps=eps or 0.0001 if justpressed(keys, last, upkey) then param = (param>=maxi-eps) and maxi or param+interval elseif justpressed(keys, last, downkey) then param = (param<=mini+eps) and mini or param-interval end return param end local function updatevalues(last, mouselambda, joylambda, length, threshold, debugmode) local keys = input.get() mouselambda = adjustparam(mouselambda, keys, last, "Number6", "Number7", 0, 1, 0.1) joylambda = adjustparam(joylambda, keys, last, "Y", "U", 0, 1, 0.1) length = adjustparam(length, keys, last, "H", "J", 2, 10, 1) threshold = adjustparam(threshold, keys, last, "N", "M", 10, 50, 2) if justpressed(keys, last, "B") then gui.clearGraphics() debugmode = not(debugmode) end return keys, mouselambda, joylambda, length, threshold, debugmode end -- Initialization local last = input.get() local mouselambda = 0.2 local joylambda = 0.2 local length = 5 local threshold = 20 local mousestart = input.getmouse() local mousexhist, mouseyhist, joyxhist, joyyhist = {}, {}, {}, {} local buttonsx = {"Right", [-1]="Left"} local buttonsy = {"Down", [-1]="Up"} -- Inverted because the y coordinate is read from the top of the screen. local debugmode = false for i=1,10 do -- Keep a history of the last 10 mouse locations and joypad buttons. Changing length only changes the window. table.insert(mousexhist, mousestart.X) table.insert(mouseyhist, mousestart.Y) table.insert(joyxhist, 0) table.insert(joyyhist, 0) end while true do mousexhist = updatemouse(mousexhist, "X") mouseyhist = updatemouse(mouseyhist, "Y") delta_x = getdeltas(mousexhist) delta_y = getdeltas(mouseyhist) mousewindow = makewindow(length-1, mouselambda) local mousexavg = windowavg(delta_x, mousewindow) local mouseyavg = windowavg(delta_y, mousewindow) local joywindow = makewindow(length, joylambda) -- Note that the joypad window and mouse window are different lengths. I assume this either doesn't matter or makes very little difference. local joyxavg = windowavg(joyxhist, joywindow) local joyyavg = windowavg(joyyhist, joywindow) joyxhist = gameinput(threshold, mousexavg, joyxhist, joyxavg, buttonsx) joyyhist = gameinput(threshold, mouseyavg, joyyhist, joyyavg, buttonsy) emu.frameadvance() keys, mouselambda, joylambda, length, threshold, debugmode = updatevalues(keys, mouselambda, joylambda, length, threshold, debugmode) -- Print all the parameters and history for troubleshooting purposes. if debugmode then gui.drawRectangle(0,0,240,160,"gray","gray") gui.pixelText(180,20,mouselambda) gui.pixelText(180,28,joylambda) gui.pixelText(180,36,length) gui.pixelText(180,44,threshold) for i=1,length-1 do gui.pixelText(180,44+8*i,delta_x[i]) gui.pixelText(20,44+8*i,delta_y[i]) gui.pixelText(200,44+8*i,joyxhist[i]) gui.pixelText(40,44+8*i,joyyhist[i]) gui.pixelText(220,44+8*i,mousexhist[i]) gui.pixelText(60,44+8*i,mouseyhist[i]) end gui.pixelText(200,44+8*length,joyxhist[length]) gui.pixelText(40,44+8*length,joyyhist[length]) gui.pixelText(220,44+8*length,mousexhist[length]) gui.pixelText(60,44+8*length,mouseyhist[length]) end end
Basically, I just copied all of the variables line-by-line. What I perhaps should have done instead was put both x and y values into a single table but... I didn't feel like it. Same request as above: run the script and tweak the parameters to your liking, then let me know what I should change.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Ask and ye shall receive! Download Mouse2Move.lua
Language: lua

-- The sign of a scalar. local function sign(x) return (x>0) and 1 or ((x<0) and -1 or 0) end -- Sums over a vector. local function sum(v) local total=0 for i=1,#v do total=total+v[i] end return total end -- Did we just press this key? local function justpressed(keys, last, button) return keys[button] and not(last[button]) end -- Inner product of two vectors. If not the same length, just uses the shorter of the two. To capture the most recent values, multiplies only the last elements. local function inner(v1, v2) local prod = {} for i=0,math.min(#v1, #v2)-1 do table.insert(prod, v1[#v1-i]*v2[#v2-i]) end return sum(prod) end -- Updates the mouse's history. local function updatemouse(history) local curr=input.getmouse() table.remove(history, 1) table.insert(history, curr.X) return history end -- Returns the difference between adjacent x values, effectively the mouse velocity. local function getdeltas(history) local delta_x = {} for i=1,#history-1 do table.insert(delta_x, history[i+1]-history[i]) end return delta_x end -- Returns a sort of weighted average of the mouse's movement based on the window function. local function windowavg(v, window) local denom = sum(window) local num = inner(v, window) return num/denom end -- Makes a window function. Exponential with time. local function makewindow(length, lambda) local window={} for i=1,length do table.insert(window, math.exp(lambda*(i-length))) end return window end -- Updates the joypad input based on the threshold and its history. Also updates and returns the history. local function gameinput(threshold, mouseavg, joyhist, joyavg) local curr=false if math.abs(mouseavg)>threshold*(1+math.abs(joyavg)) then -- "1+math.abs(joyavg)" is just a guess. It maybe needs to be refined. curr=true end local lorr = sign(mouseavg) local buttons = {"Right", [-1]="Left"} if curr then joypad.set({[buttons[lorr]]=curr}) end table.remove(joyhist, 1) table.insert(joyhist, curr and lorr or 0) return joyhist end -- Adjusts a single parameter according to whether we press some keys. local function adjustparam(param, keys, last, downkey, upkey, mini, maxi, interval, eps) eps=eps or 0.0001 if justpressed(keys, last, upkey) then param = (param>=maxi-eps) and maxi or param+interval elseif justpressed(keys, last, downkey) then param = (param<=mini+eps) and mini or param-interval end return param end local function updatevalues(last, mouselambda, joylambda, length, threshold, debugmode) local keys = input.get() mouselambda = adjustparam(mouselambda, keys, last, "Number6", "Number7", 0, 1, 0.1) joylambda = adjustparam(joylambda, keys, last, "Y", "U", 0, 1, 0.1) length = adjustparam(length, keys, last, "H", "J", 2, 10, 1) threshold = adjustparam(threshold, keys, last, "N", "M", 10, 50, 2) if justpressed(keys, last, "B") then gui.clearGraphics() debugmode = not(debugmode) end return keys, mouselambda, joylambda, length, threshold, debugmode end -- Initialization local last = input.get() local mouselambda = 0.2 local joylambda = 0.2 local length = 5 local threshold = 20 local mousestart = input.getmouse() mousestart=mousestart.X local mousehist, joyhist = {}, {} local debugmode = false for i=1,10 do -- Keep a history of the last 10 mouse locations and joypad buttons. Changing length only changes the window. table.insert(mousehist, mousestart) table.insert(joyhist, 0) end while true do mousehist = updatemouse(mousehist) delta_x = getdeltas(mousehist) mousewindow = makewindow(length-1, mouselambda) mouseavg = windowavg(delta_x, mousewindow) joywindow = makewindow(length, joylambda) -- Note that the joypad window and mouse window are different lengths. I assume this either doesn't matter or makes very little difference. joyavg = windowavg(joyhist, joywindow) joyhist = gameinput(threshold, mouseavg, joyhist, joyavg) emu.frameadvance() keys, mouselambda, joylambda, length, threshold, debugmode = updatevalues(keys, mouselambda, joylambda, length, threshold, debugmode) -- Print all the parameters and history for troubleshooting purposes. if debugmode then gui.drawRectangle(0,0,240,160,"gray","gray") gui.pixelText(180,20,mouselambda) gui.pixelText(180,28,joylambda) gui.pixelText(180,36,length) gui.pixelText(180,44,threshold) for i=1,length-1 do gui.pixelText(180,44+8*i,delta_x[i]) gui.pixelText(200,44+8*i,joyhist[i]) gui.pixelText(220,44+8*i,mousehist[i]) end gui.pixelText(200,44+8*length,joyhist[length]) gui.pixelText(220,44+8*length,mousehist[length]) end end
I've briefly run this in debug mode to verify that it is at least mostly working, but you'll want to tweak the parameters yourself. There are four main parameters: mouselambda, joylambda, length, and threshold.
  • mouselambda affects how much we weight old mouse movements. Its default is 0.2. Set it to 0 and all mouse movements are equal. Its maximum value is 1, making it heavily weighted toward new movements. (Behind the scenes, each change in x is weighted by e^(-lambda) times the more recent change in x, so if lambda=1, the most recent delta-x gets a weight of 1 and the second most recent gets a weight of just 0.368, quickly going to zero.) To raise or lower its value, press 7 or 6, respectively.
  • joylambda is much like mouselambda, deciding how much we weight toward recent joypad inputs. Its default is 0.2. Set it to 0 and all joypad presses are equal. Its maximum value is 1, producing the same weighting strongly toward new joypad movements in exactly the same way as the mouselambda parameter. To raise or lower its value, press u or y, respectively.
  • length is how far back into the history we look. I keep the last ten values of each but truncate the window to this length. Its default is 5. Set it to 2 and only the most recent mouse movement matters (and if it's working correctly, the two lambda parameters are then irrelevant). Its maximum value is 10, which might cause a noticeable time lag, especially if your lambdas are small. To raise or lower its value, press j or h, respectively.
  • threshold is basically how far you have to move the mouse over the course of one frame in order for the script to register a left or right button press. This is the parameter I'm least certain about. I've allowed it to range from 10 to 50, with 10 being very sensitive to mouse movements and 50 being very insensitive. Its default is 20 and you can raise or lower its value in increments of 2 by pressing m or n, respectively. I also made a guess as to how it might be implemented, with the mouse movement needing to exceed the threshold plus the weighted average of previous joypad presses.
  • Additionally, I've included a "debug mode", which can be accessed by pressing b. This displays all parameters and recent mouse movements, joypad buttons, and mouse positions.
Very basically, setting mouselambda=0, joylambda=0, length=10, and threshold=50 makes input weak and laggy. Setting mouselambda=1, joylambda=1, length=2, and threshold=10 makes input strong and twitchy. Having said all that, I have an assignment for you: Run the script with the game of your choice (Revolution X?) and freely change the parameters by pressing 7, 6, u, y, j, h, m, and n until it's working to your satisfaction. Then switch to debug mode and tell me what the parameters are so I can change the defaults, ranges, and intervals. If there are any basic issues with the program, such as wanting more or less maximum sensitivity, more fine tuning, or a longer history, I can make those changes easily so don't be afraid to tell me about them! If there are more complex issues, please bring them to my attention anyway and maybe I'll discover there's something simple wrong with my assumptions or implementation. But in general, no promises. If we're lucky, the game accepts input frame-by-frame, rather than in pairs or with acceleration or something else that might affect how mouse movements translate to joypad movements. If that ends up being the case, troubleshooting my script will be rather difficult. Hope this works right out of the box! Edit: I've updated the threshold parameter to range from 10 to 50 in intervals of 2 because I perceived it wasn't sensitive enough. I also overhauled the updatevalues function, consolidating all the if statements into a new adjustparam function. Edit 2: I probably should have looked up a video of the game before starting this. For some reason, I assumed Revolution X was a top-down shoot-'em-up with the ship fixed at the bottom of the screen. As such, I only programmed in motion for the x axis. Oops. Adding in y axis functionality probably won't take all that long, but I kind of want to take a break so please be patient.