Friday 21 November 2014

Further Levels of Abstraction

Since Monday I've been coding plenty but I've added virtually nothing to the game. Instead I'm just been raising the level of abstraction of everything.

Now instead of having a function that goes through a lot of different if statements to figure out what your characters should do next, I have a function that runs through an array of functions that each check whether you should do a particular thing. The array is passed an object that has an order property that tells the array where to put it in the chain.

So now instead of getting to a statement that says, "If you've bought the Stairs feature then check if you should go down stairs," that check is simply never performed and getting the Stairs feature inserts that check into the array of things to check.

I'm just about to implement a similar approach for rolling for treasure. When you start a new game each enemy you loot will just give you the coins. When you learn items a function will be added to the array beforehand that rolls to see if you should get an item instead.

There are a few more options in my working version, but mostly they are just options to buy things that were already in the game - like buying the feature that your character will automatically return to town when their inventory is full and things are marked for sale.

Also, I saw a sign in the elevator in my building that had instructions to buy tickets to the christmas party. It said to pop the ticket money into an envelope with your name and phone number and put it into the appropriate place. I was honestly baffled for a split second while I tried to figure out why they would have said to "pop" money into something when they clearly should have said "push."

Thursday 20 November 2014

Maddening Javascript

Does this look right to you:

function (x) {return function() {_.buy(x)}}(i)

Last night I ran into a problem that I didn't quite know how to solve. I've recoded features so that they have multiple stages contained within them. For example, instead of having a separate feature for each rarity of items, I have a second feature, Items, that you can buy all the ranks through. First I recoded it so that the stages were linear - the feature kept track of which stage it was on by use of an integer - then I decided that I should redo it so that it's non-linear. This is all in preparation for adding a town with upgradeable buildings, so you can see how I would want to have multiple choices of upgrades at the same time.

So when you click a feature I need the game to bring up a menu, and I need to create that menu by running through the stages and seeing which ones are currently available so it can add them to the list.

The game has only one menu object. If you have a menu open and you click another thing that opens a menu then the first menu will disappear, that's because it's actually the same menu that is just relocated and drawn. To create a menu you have to pass the menu an array. Each element of the array has to either be a string or an object. If it is a string then the string is appended to the HTML of the menu. If it is an object then the game creates a div and sets its innerHTML to the name property and adds an event listener that runs the Func property on a click.

So I looped through the stages of the feature in a for loop and whenever I came to an available feature I pushed two things to the array that would generate the menu. First I pushed some text that describes the upgrade and its cost. Then I pushed the object to make the menu, which has a name of something like "Buy this" and a Func of function() {_.buy(i)} where i is the variable I'm looping on.

When I did this, the menu came up correctly and it correctly offered to let me buy common items. However, when I clicked the option, it bought legendary items instead.

Because the function that I attached to the menu was defined inside the same function where the loop was happening, when it went to pull up that function it did it within the scope of the function that created the menu. That means that instead of using what i was at the time when that menu option was created, it used what i was now, after all menu options were created, which is the last stage on the list.

At this point I actually just got up from my computer and walked away. The logic of passing functions to functions has occasionally caused me problems, but I actually just couldn't think of what I should do about this.

Once my head was away from the frustration of the situation and into the cold air, the answer was clear, and not that challenging. All I had to do was make a function that returned the function and it would fix the value being passed at that moment. As long as I actually execute the function that I'm passing i to, the value will be locked in at the time of execution and the game won't look back to see what i is when the menu item is clicked.

The resulting code has the look of ln ex, but it does its job.

Anyway, yes, I'm working on a town with upgradeable buildings, the primary purpose of which - in it's initial implementation - will be to get a guildhall so that you can learn a class and then get class abilities.

Monday 17 November 2014

Yes It Is!

On the advise of commenter Chris I have decided to leave making sprite sheets for another day. I made a couple finishing touches now I have it for you.

Click Here For Fun.

I've twice now called this an "early alpha." I don't think it's riddled with bugs, or at least not crash bugs. But it is very feature-light. I'm sharing it now for two reasons:

  1. There are a lot of things I could be doing in terms of further development. I'd like to hear about what I should be doing and get ideas from people.
  2. I have started about a dozen browser games and abandoned them all before I had any kind of product. I sort of committed myself to putting something out there.
This is an idle game, not a carpal tunnel syndrome game. That doesn't mean that it has no interactivity, but when you start, you are not missing the thing to frantically click on, it isn't there.

I have tested this on Chrome and Firefox. I have no idea what crazy things it might do in other browsers, but working on that cross browser compatibility is about the least fun thing I could imagine doing with this project, so that might be a long way off. It also unapologetically uses LocalStorage and other contemporary browser stuff. If you are using a less than modern browser, I have no idea if it will run.

Anyway, don't expect to get a huge amount of play out of this, and bear in mind that I most likely will blow up your save game at some point in the future.

If you want to blow up your own save game, you can open a console and type in:

$$.blow_up_the_world()

And that will do it for you.

Enjoy and send feedback, either through comments or email or just by talking to me because half of you know me.

Heinous Error - Still On Track

Ten days ago I said I was going to release an "early alpha" of an idle game today. This post is not a post to say that I am going to delay that release, but it is also not a post doing the release. I really do plan to make another post this very day on this very blog releasing it.

But I'm not quite there yet.

The one thing I'd like to fix before I get it out requires a little bit of fiddly graphics work, but I think I should have time to get it done this evening. Frankly, if I understood more about how browsers work and how they cache images I might know that I don't have to do the thing I want to do at all, but I'm going to go ahead and do it anyway, because I'm pretty sure it can't be worse, and could be quite a bit better.

Imagine you have an image on your screen which is going to change fairly frequently from one thing to another. One way to do that is to change the image every time you want it to change. Another way is to make one image which is twice the width of the two images, and simply reposition it within an element that cuts off the excess.

I started by doing it the first way, and I want to redo things so that it works the second way.

There are two reasons why I want to do this. First of all, it's possible that the size of the combined image is smaller than the combined sizes of the separate images, but it doesn't seem very possible that its larger. Worst case scenario it is literally no easier to save than the two images side-by-side, best cast it seems like the compression can do better with a larger image than with two smaller ones. Now I'm not actually very familiar with image compression, and I'd almost be surprised if someone couldn't, as an exercise, come up with two very particular images whose sizes added up to more than the sum of their parts for any given compression scheme, but generally my understanding of image compression is that the more uniform the image, the better it compresses - a solid white image is going to compress beautifully, for example. Well, these images I am saving have a lot of white space, so hopefully the compression can do something with that.

Secondly, though, if you download one big image then you get one big image once and just hold onto it for the entire time you run the game. If you swap between images then you need to get one, then the other, then the first one again. As I said, I don't really understand how browsers cache images, but I do know that when I was actively running the game and changed one of the image files the image on the screen would not change, but if it later swapped to it, it would change. That means that at the very minimum every swap between images means a check to see if they have changed with a hash or something, and at worst it means downloading the image all over again.

I looked down the images I am working with and the largest one I have so far is 7KB. That's not really very much, but what if people like the game and share it with their friends? What if this time next week fifty are playing? And if the game swaps that image onto the screen on average every minute? Well, that's 500MB a day. Google doesn't actually publish the limit on accessing info from their free drive accounts, but I read someone doing a test on it that suggests that this might start brushing up against the point where they notice. I do plan to pay for web hosting in the near future, but I haven't actually started doing that yet, and I don't want to get Google upset at me.

For the code this will mean only minor alterations, but for the actual images this means I have to open them up and paste them together. That probably happens after bedtime, so don't expect to see the game up until about 10:00 or 11:00 EST.

Wednesday 12 November 2014

Saving the Game

I mentioned last week that my big goal before sharing my idle game project was to put in saving. I'm working on it, and encountering three challenges.

First, figuring out a way to sensibly convert the information into a string. Local storage will let me store strings, so that's what I've got to work with. I don't just have a series of numerical values to store. It's not just number of widgets produced, number of each widget producing thing bought, and a bunch of booleans for upgrades acquired, Instead I have names of things that are generated as the game goes on. I don't start knowing how many things I need to store or what kind of information I'll have attached to each thing. I can obviously do whatever I want with strings of course, but this is the kind of fiddly real-life stuff that I hate doing. String manipulation is awful.

Second, I don't want to lock myself out of doing different things in the future. If I add another stat to the game, add new features or add another gear slot or something like that I don't want to have to delete old saves. Usually this is accomplished by adding new things to the end, but I don't have a well defined "end" to add them to.

Third, local storage, as I understand it, is usually stored per domain. Because I'm currently hosting things on google drive, that means I'll be sharing my local storage with everything else on google drive. I don't think this is a really big problem, but it might be a problem in a way I'm not aware of. Maybe it's time I get around to registering humbabella.com or something of that sort.

Anyway, one of the takeaways is that this is going to have the most hackable savegames since Candy Box 2.

And, as there always is, there's a lot of executing, finding out I missed a semi-colon, putting it in, executing again, and being alerted to yet another missing semi-colon. That's a constant. Strangely, though, the last few days have been full of instances of things actually working. I type in a bunch of new stuff in several different places and run it and it works exactly how I wanted it to.

It's like I'm getting better at doing something by practicing.

Monday 10 November 2014

Cookie Clicker Beta - Grandma-Type Boosts

We all know what I'll call Grandma-Type Boosts, boosts that you unlock by getting 15 of an object that double the effectiveness of grandmas and give you a type of grandma among the grandmas on your screen. In the beta these have a neat change:


I really like this. Something Cookie Clicker could use more of is things that interact with one another.

Well, naturally I had a spreadsheet going, and I noticed that the value I was calculating for my buildings wasn't matching their actual output. This affected only those buildings with the grandma bonus, so I was fairly sure the grandma bonus was not doing what it said.

I tried adding in the bonus before other doubling multipliers, but that didn't work. After a few experiments I resorted to looking at the code. The game computes the cookies per second of each object by adding up the number of doubling upgrades, raising two to the power of that, and multiplying that by the base value of the object. How do grandmas fit in? Well, if you have the upgrade then it multiplies the number of grandmas by 0.01 and then adds that in with the doubling upgrades. But that doesn't increase the CpS by 1% per grandma, that multiplies the CpS by 2 to the power of 0.01 per grandma.

That means that 50 grandmas don't give 50% more, they give about 41% more. On the other hand, 200 grandmas give four times as much rather than three times, and 300 grandmas give eight times instead of four times.

I don't really have a way to know whether Orteil meant for grandmas to do what the tooltip says or whether Orteil meant for the tooltip to express what grandmas do. But since grandmas give this bonus to all objects except for cursors and themselves, this basically means that the bonus from grandmas is exponential rather than linear, every 100 grandmas doubles your overall output, each grandma is a 0.7% increase.

We know that the cost of grandmas, like other buildings, increases exponentially as well, so this doesn't get too out of hand. Since each grandma gives 0.7% more cookies per second, but each one costs 15% more, the marginal value of each grandma decreases by 13.5%. That means that if it took you one minute to save up for a grandma, the next one will take 14.2% longer, about 68.5 seconds. One hundred grandmas later, all other things being equal, you'd have to wait about 407 days to save up for the next one.

So this isn't going to ultimately make a huge difference, but if you consider the difference between getting 14.2% compound interest and 15% compound interest, you'll know that as the numbers get bigger you'll start noticing a big difference. Because that difference is ultimately translated back into cookies to spend on a resource with an exponential cost, the total will only end up being a little bit higher.

Still, it makes a big difference to the relative value of grandmas and prisms as the game goes on, and that's kind of nice since grandmas are so key to the plot of Cookie Clicker.

Friday 7 November 2014

Cookie Clicker Beta - And Big News!

I've been playing through Cookie Clicker again for the last month or two and I am thoroughly at endgame. All I have left to do is accumulate 400 cursors and harvest up a total of an octillion cookers. That will take another couple of months, I think.

In the meantime I decided to check out the beta build, and I have to say it's pretty exciting. Building prices and production values have been redone. There are new buildings; not just slapped on the top at even higher cost, but in the sequence. Factory and mine are switched, and - no spoilers - you don't go straight to space from there.

I don't know what the revision to the legacy system is, but there is something to it, so I'm racking up my trillion cookies to see what happens. It's got a legacy button in the upper right corner that has a progress bar to a trillion cookies to show you how you are doing. I am looking forward to ascension, even though it's probably a really bad idea to do it at one trillion.

And on the idle game front, I've put together a pretty decent first approximation of my latest project. The thing it doesn't do, though, is save your game, so it needs a little work. Fortunately I am going to actually have a few hours to myself in the near future, so I hope to get that done and unveil the "alpha" Monday after next.

I've already shared the version I have with someone and they remarked that it was like a real game, so I'm a little excited.

Wednesday 5 November 2014

Habit One - Radically Alter Your Entire Worldview. Do It Now.

There is no piece of advice in the "Habit One" section of the Seven Habits of Highly Effective People that couldn't be seen as a piece of good advice. If you are interested in learning about the habits, I can save you a lot of time. Here is a summary:
When something happens, choose to act in a positive way instead of letting the thing that happened dictate your reaction.
Yeah, that's all you have to do, just be positive about things all the time. Now, he clearly differentiates his approach, which he calls proactivity, from simple positive thinking. He thinks you should be clear and honest about challenges. You know, positive thinking but don't be unrealistic in your positivity - positive thinking but everyone but me is dumb.

And that's it. The chapter certainly has examples of people who were positive and who did pretty well. He makes a lot of reference to Victor Frankl who manages to not only survive but flourish in a Nazi death camp by choosing how he would act in the face of the terrors. That's an inspirational story of a person who didn't let even the worst circumstances dampen his spirit.

First, let me complain about positivity. We are talking about a Nazi death camp here. We really don't know how many positive thinkers were killed in these camps precisely because they were positive about their experiences and thus threatening to the people running the camps, and for all that Covey writes about how inspiring it is when someone faces death with dignity, those people still end up dead. Covey may have a fantasy land afterlife story in which things work out okay, but that's just make believe. Face death with as much dignity, indignity, happiness, sadness, courage or dread as you like, one day the universe will be as it would have been if you had done the other. Nothing persists forever.

Now that I've vented my generalized negativity, let me say something bad about the book specifically. The advice really is to just do this. Like, "Hey, did you know that no matter how bad things are, you still get to decide how you react to them?" Yes, I did realize that. I'm sure that some people don't realize that, but reading it in a book isn't going to help.

The book is called the seven Habits of highly effective people, so you'd think it would include some kind of habits in it. Maybe a hint as to what behaviours you could engage in to acquire these habits. Here is Covey's list:

  1. For a day listen to your language and the language of those around you and see how often you say things like, "I can't" and "If only"
  2. Think about a situation you are going to encounter and how you could approach it proactively. Then do that.
  3. Identify a problem you have, figure out how you can use the advice of chapter to work on it. Do that.
  4. For a month, just be the way I say to be in here, maybe you'll like it?

My friends used to chant, "do it! do it! do it! do it! do it! do it!" very quickly as a kind of alternative to "come on." Aside from number 1, that's what Covery's got. He's got "Come on! Just be proactive already!" and that's all.

In my first post about the book I compared self-help books to fad diets, and this is really helping with that comparison. The chapter is long on things that are supposed to leave the reader feeling inspired and short on mundane approaches to acquire a new habit or skill. The advice basically covers this problem off itself. Since the advice is to be proactive instead of being acted upon, why don't you be proactive about being proactive instead of being reactive and waiting for something to come along and make you proactive?

Is there a point to a book of advice that begins with the advice to pull yourself up by your bootstraps?

Tuesday 4 November 2014

Shouting at People

I sometimes post on here about how I essentially make a sport of arguing with jerk and morons online. It's half-in-jest, I do treat it like a sport but my anger that someone on the internet is wrong is all too embarrassingly real.

What I don't do anymore is go after people for the way they've chosen to express themselves. At least, I hope I don't do that. We live in a world with a lot of different people and they express themselves in different ways. If someone has an idea to get across then I'm interested in the idea, not in how they tried to get it out there. Sometimes if I honestly misunderstand someone I will explain what it was about their expression that caused the misunderstanding. Sometimes when someone is attracting a lot of anger because of how they chose to express themselves I'll give them a hint that their manner of expression, and not their content, is the problem.

What I don't want to do is say, in the words of Voltaire, "I agree with your opinion but I will fight to the death the way you said it." If someone wants to hire me as a communications consultant, that's fine, but otherwise I can let them say what they want to say without providing my unsolicited advice.

Which brings me to a person who expresses things very differently that I would. They swear at people and laugh at people. They call people names. When someone says something stupid, they are a lot more likely to say, "Hey, that's stupid" than I am and a lot less likely to try to break the whole issue down to make a rigorous point.

I don't enjoy reading posts by this person, I often feel they've been mean, I sometimes feel they are escalating the conversation into a fight. I am also very glad they are doing it.

Philosophy is, apparently, the malest, whitest subject you can study, and I am definitely a philosopher. Sure, I'm a problem child of philosophy, a rebellious one, but if my discourse exists in opposition to philosophy then it is still defined by philosophy. I turn logic and fallacies back on the people who use them, but they are still fundamentally the things I deal in. I talk to other people from within a very narrow set of rules about how we are supposed to talk that excludes the ideas of a huge number of people.

So while I may be tempted to think, "Oh, don't get angry, that's not the most effective way to win out in this situation," I am really trying to impose my own internal process on that person. In reality people say and do things that make sense to be angry about.

When I read this commenter's posts, I find myself put off by their manner of expression. But at the same time I stomach that manner of expression long enough to get the point they are making, and they have some very good points. It's nice because it makes me more confident that when other people are being jerks I'm not dismissing them simply *because* they are jerks, but rather I'm able to see past that.

It's good practice to not try to police other people's tone when I should be trying to understand them and address what they are actually saying. And sometimes using a different manner of expression allows someone to make a point that they just never could have made if they wrote and spoke the way I do. I like hearing other people's ideas.

Monday 3 November 2014

Back to Our Darker Purpose

I started playing Our Darker Purpose again. This game definitely has some things going against it from my perspective, but I really do like it a lot. I was watching a bit of youtube video of someone who is much better than me playing it, but his analysis of items and perks was pretty lacklustre, so I thought I might as well write some stuff since that's what I do.

So if you wanted to read a criticism of someone you don't know's perk-weighting heuristics about a game you don't play, you've come to the right place. More incredibly, I basically agree with what he said, and really I only take issue with the fact that a comment he made at the end revealed that he believes what he believes for the wrong reasons. This has got to be very important.

The particular comment was that life perks are not very good. In Our Darker Purpose each time you level up you get to choose between two perks. There are three perks on the list that give a flat 50 life with no other effect. You begin the game with 100 life, so 50 might seem like a lot, but the youtuber in question said that these perks weren't very good, and he was right.

In most RPGs increasing your health is very good and often its even priority one for people interested in endgame play. What are the factors that make health so good?
  1. Getting one-shotted or stun-locked out. If you can take more than your maximum health from a single attack or can be stunned long enough to lose your maximum health while not in control, you are living in a constant binary alive/dead state. This is not desirable for long term survival. Having enough health to live through an attack gives you leeway to increase your mistakes allowable from zero to more than zero.
  2. Avoid overhealing. If you can get healed for a huge fraction of your maximum health at a time, then more health means less healing waste.
  3. Starting fight as maximum. In many RPGs you are always going to heal up to full using some essentially renewable resource before each dungeon, boss, or fight. The lifespan of your lifepool is much shorter, so you use maximum life over and over.
What makes Our Darker Purpose different? Nothing does enough damage to one-shot you. Overhealing is something you can almost always avoid. You don't get restored to full health. Maximum health allows you to carry more health from one floor to the next if you are at full, but for the most part it's just health.

In Our Darker Purpose you have to see your life total over the course of the entire game. You start the game with 100 life and three drinking boxes which restore 20 each. That's 160 life to work with. You can get more in various ways, but each time you gain life you add that to the total life for the game and each time you get hit you subtract it off. There are no resets until the game is over.

So when the youtuber said that health perks aren't great, I tend to agree. Having 50 more life to play the game with is a good thing, but it doesn't compare favourably with most other perks like doing 20 more damage or moving faster. But then he said something that was very wrong. He said that maybe if you had a Doctor's Note it would be worth it because it would require fewer juice boxes to heal up again.

It really doesn't matter how many juice boxes it requires to heal up that 50 life, because it's still gone once it's gone. If your juice box value is up to 50 then you can recover that 50 life for just one box, but that's still one fewer box to spend in the rest of the game. It's gone and you aren't getting it back. Juice box value is unaffected by your life total, so unless it gets up to the point where you are going to have overhealing problems, a high juice box value does not make the 50 life perk better.

Of course there are perks that are just plain bad, so generally I would say that I take a life-up perk once a game. It's not the worst, it's just not really very good. But that has nothing to do with whether you have or have not found a Doctor's Note.