Posts Tagged With: Today’s World

Hacking Roller Coaster Tycoon with Genetic Algorithms

I used to play a ton of Roller Coaster Tycoon when I was a kid. I loved the game but I was never very good at making the roller coasters. They always felt too spread out, or too unnatural looking. As a ten year old I idly wondered about writing a computer program that was really good at playing the game. What sort of parks would it make? How would a computer approach the freedoms inherent in an empty park? What would we learn about the game engine from doing so?

A cool coaster

Sadly, this one wasn't generated by a computer

In case you're not familiar, Roller Coaster Tycoon is a amusement park simulation game most notable because the entire game was written in x86 assembler by Chris Sawyer.

Finally a few months ago, I had the tools and the free time available to work on this, and I made some progress toward writing a program that would generate cool looking roller coasters. Let's examine the parts of this program in turn.

Interacting with the Game

So let's say you have a program that can generate roller coasters. How do you actually put them in the game, or integrate them into your parks?

Fortunately, Roller Coaster Tycoon has a format for saving track layouts to disk. Even more amazingly, this format has been documented.

4B: departure control flags
4C number of trains
4D number of cars per train
4E: minimum wait time in seconds
4F: maximum wait time in seconds

Once you decode the ride data, it follows a format. Byte 0 stores the ride type - 00000010 is a suspended steel roller coaster, for example. Some bytes indicate the presence of flags - the 17th bit tells you whether the coaster can have a vertical loop. And so on, and so on.

To compress space, RCT used a cheap run-length encoding algorithm that would compress duplicates of the same byte. But once you encoded/decoded the file, it was super easy to get it running in the game.

So, great! I could write my coaster generator in any language I wanted, write out the file to disk, then load it from any of the parks.

Getting Track Data

There are a lot of track pieces in the game, and I needed to get a lot of data about each of them to be able to make assertions about generated roller coasters. As an example, if the track is currently banked left, which pieces are even possible to construct next?

A steep upward slope track piece increases the car height 4 units. A sharp left turn would advance the car 3 squares forward and 3 squares left, and also rotate the car's direction by 90 degrees. I had less than zero interest in doing this all by hand, and would probably make a mistake doing it. So I went looking for the source of truth in the game..

OpenRCT2

Literally the same week that I started looking at this, Ted John started decompiling the Roller Coaster Tycoon 2 source from x86 into C, and posting the results on Github. More importantly (for me), Ted and the source code actually showed how to read and decompile the source of the game.

The repository shipped with an EXE that would load the C sources before the x86 game code. From there, the C code could (and did, often) use assembler calls to jump back into the game source, for parts that hadn't been decompiled yet.

This also introduced me to the tools you use to decompile x86 into C. We used the reverse engineering tool IDA Pro to read the raw assembly, with a shared database that had information on subroutines that had been decompiled. Using IDA is probably as close as I will come to a profession in code-breaking and/or reverse engineering.

Most of the time with IDA involved reading, annotating the code, and then double checking your results against other parts of the code, the same way you might annotate a crossword puzzle. Other times I used guess and check - change a value in the code, then re-run the game and see what specifically had changed, or use debugging statements to see what went on.

So I started looking for the track data in the game. This turned out to be really, really difficult. You would have a hunch, or use the limited search capability in the game to search for something you thought should be there. Ultimately, I figured out where the strings "Too high!" and "Too low!" were being called in the engine, figuring that track height data would have been computed or used near those points.

This was only part of the solution - it turns out that track data is not stored in one big map but in several maps all around the code base. Some places store information about track bank, some store information about heights and it's tricky to compile it all together. Ultimately, I was able to figure it out by spending enough time with the code and testing different addresses to see if the values there lined up with the pre-determined track order.

Visualizing rides

With a genetic algorithm you are going to be generating a lot of roller coasters. I wanted a quick way to see whether those roller coasters were getting better or not by plotting them. So I used Go's image package to draw roller coasters. To start I didn't try for an isometric view, although that would be fun to draw. Instead I just plotted height change in one image and x/y changes in another image. Running this against existing roller coasters also revealed some flaws in my track data.

A fitness function

A good fitness function will have penalties/rewards for various pieces of behavior.

  • Is the ride complete?

  • Does the ride intersect itself at any points?

  • Does the ride respect gravity, e.g. will a car make it all the way around the track?

  • How exciting is the ride, per the in-game excitement meter?

  • How nauseating is the ride, per the in-game excitement meter?

The first two points on that list are easy; the last three are much more difficult. Finding the excitement data was very tricky. I eventually found it by getting the excitement for a "static" ride with no moving parts (the Crooked House) and searching for the actual numbers used in the game. Here's the function that computes excitement, nausea and intensity for a Crooked House ride.

sub_65C4D4 proc near
or      dword ptr [edi+1D0h], 2
or      dword ptr [edi+1D0h], 8
mov     byte ptr [edi+198h], 5
call    sub_655FD6
mov     ebx, 0D7h ; ''
mov     ecx, 3Eh ; '>'
mov     ebp, 22h ; '"'
call    sub_65E7A3
call    sub_65E7FB
mov     [edi+140h], bx
mov     [edi+142h], cx
mov     [edi+144h], bp
xor     ecx, ecx
call    sub_65E621
mov     dl, 7
shl     dl, 5
and     byte ptr [edi+114h], 1Fh
or      [edi+114h], dl
retn
sub_65C4D4 endp

Got that? In this case 0xD7 in hex is 215 in decimal, which is the ride's excitement rating. I got lucky that this value is static and not changed by anything, which meant I could search for it from outside the binary. This is then stored in the ride's location in memory (register edi), at the offset 0x140. In between there are a few subroutine calls, which shows that nothing is ever really easy when you are reading x86, as well as calls to functions that I have nothing besides hunches about.

Anyway, when you turn this into C, you get something like this:

void crooked_house_excitement(rct_ride *ride)
{
    // Set lifecycle bits
    ride->lifecycle_flags |= RIDE_LIFECYCLE_TESTED;
    ride->lifecycle_flags |= RIDE_LIFECYCLE_NO_RAW_STATS;
    ride->var_198 = 5;
    sub_655FD6(ride);

    ride_rating excitement  = RIDE_RATING(2,15);
    ride_rating intensity   = RIDE_RATING(0,62);
    ride_rating nausea      = RIDE_RATING(0,34);

    excitement = apply_intensity_penalty(excitement, intensity);
    rating_tuple tup = per_ride_rating_adjustments(ride, excitement, intensity, nausea);

    ride->excitement = tup.excitement;
    ride->intensity = tup.intensity;
    ride->nausea = tup.nausea;

    ride->upkeep_cost = compute_upkeep(ride);
    // Upkeep flag? or a dirtiness flag
    ride->var_14D |= 2;

    // clear all bits except lowest 5
    ride->var_114 &= 0x1f;
    // set 6th,7th,8th bits
    ride->var_114 |= 0xE0;
}

And we're lucky in this case that the function is relatively contained; many places in the code feature jumps and constructs that make following the code pretty tricky.

So this one wasn't too bad, but I got bogged down trying to compute excitement for a ride that had a track. The function gets orders of magnitude more complex than this. One positive is, as far as I can tell, excitement and nausea ratings are wholly functions of overall ride statistics like the vertical and lateral G-forces, and there's no accumulator per track segment.

Most of the computation involves multiplying a ride statistic by a constant, then bit shifting the value so it can't be too high/influence the final number by too much.

And sadly, this is where the project stalled. It was impossible to test the C code, because the track computation functions were buried four subroutines deep, and each of those subroutines had at least 500 lines of code. Decompiling each of these correctly, just to get to the code I wanted, was going to be a massive pain. There are ways around this, but ultimately I got back from vacation and had to focus on more pressing issues.

Conclusion

You can hack Roller Coaster Tycoon! There are a bunch of people doing interesting stuff with the game, including improving the peep UI, working on cross compilation (you can play it on Macs!), adding intrigues like the possibility of a worker strike, removing limitations based on the number of bytes (you can only have 255 rides, for example), and more.

It's been really fun having an utterly useless side project. I learned a lot about registers, calling conventions, bit shifting tricks, and other things that probably won't be useful at all, for anything.

I will definitely revisit this project at some point, hopefully when more of the game has been decompiled, or I might try to dig back into the x86/C more on my own.

Liked what you read? I am available for hire.

Levelers

I read a great blog post in college, and sadly I can't find it now, so I'll summarize. It showed a picture of Hearst Castle, and a photo of an average middle-class home, and the text of the post went something like:

  • One of these can travel across the country in 6 hours.

  • One of these can communicate with friends and family on the other side of the country instantaneously.

  • One of these can receive news from the other side of the globe instantaneously.

  • One of these can watch movies in full color and sound in their own home.

  • One of these can expect to live to age 80, and give birth to a child without serious risk of complications.

  • One of these can wash an entire day's worth of dishes in 40 minutes with no human effort.

Hearst Castle. Photo credit Beedie Savage. Creative Commons BY-NC 2.0

The point is, even though Hearst was one of the richest men on the planet, in many ways the average middle class person today enjoys a better quality of life, due to advancements in technology and medical science since the 1940's.

It's really exciting when technologies (air travel, the Internet, smartphones, appliances) become accessible and cheap enough that everyone can enjoy them. But it's even more exciting when the high end of the market comes within reach of most people.

Let's say Bill Gates wants a better smartphone - say, with a longer battery, or with a UI that does exactly what he wants. He's basically out of luck - he can't throw enough money at the problem to get a better phone than what's currently on the market. A phone you can afford is the best one that Bill Gates can buy. I call products like these levelers, because they level the playing field for everyone. It's easy to think of other examples - most software/apps/websites, beers, movies, and more.

So I got excited yesterday when I read about a product that might level the playing field for another experience that's currently only for rich people.

To get a sense of what JauntVR has built, their technology is described as “an end-to-end solution for creating cinematic VR experiences.” Their camera films real world footage from everything surrounding it, and their software stitches those clips together to create a seamless experience. The wrap-around film capture means that you’re able to swivel in any direction to maintain that feeling of “actually being there.”

Rarely is the window into the future as stunningly clear as was for me for the next few minutes inside that Oculus Rift. So what was it like inside? (Keep in mind: writing about the mindshock of live action VR, is quite like trying to share a photograph of your favorite song. Words simply cannot do justice.)

I settled into a swivel chair at the center of a dark surround-sound equipped room, slid on the Rift, and instantly I was no longer in Palo Alto but sitting inches away from a string quartet performing inside a luxurious concert hall. Stunned by the realism (it is real after all!) I began turning in every direction, to discover that behind me I could see the full venue where an audience would sit. Then I understood where I was – right where the conductor would stand. Not a bad seat and with the surround sound so clear, I felt as if I was actually there.

Just like that - millions of people can suddenly all sit courtside for the NBA Finals, or even hover above the court, and suddenly, my view of the game isn't from section 400, it's as good as Jack Nicholson's.

This got me really excited for the future of virtual reality, and the new things that people are going to build on top of it. I can't wait to see what people build.

Liked what you read? I am available for hire.

It begins

Email from my alma mater:

Dear Members of the Claremont McKenna College Community,

I am writing to update you on an important action taken by the Board of Trustees at its meeting on March 9, 2013. In particular, the Board acted to end the College’s “No Packaged Loan” financial aid policy. Beginning with the fall 2014 entering freshman class, the College will reinstate its former practice of including reasonable loan amounts of up to $4,000 per year in the financial aid package for need-based students. This policy change will not affect any current students during their remaining time at CMC, nor will it affect new students enrolling in fall 2013.

This decision was not taken lightly, as we know that there are challenges and pressures that some families face regarding the affordability of a college education. However, our current situation is best understood in view of the College’s long-standing commitment to need-blind admission and to meeting the financially demonstrated need of all admitted, domestic freshman students. The College’s Strategic Plan identifies need-blind admission as one of our most important values, and highlights the importance of insuring our need-blind policy is financially sustainable over the long term. Therefore, I wanted to take this opportunity to briefly discuss the background of the No Packaged Loan policy, and the reasons why the Board determined it was necessary to end the policy at this time.

The College adopted the No Packaged Loan policy in the spring of 2008, just prior to the global financial crisis, at a time when a number of CMC’s peer colleges and universities were implementing various forms of no-loan or reduced-loan policies. At that time, the College completed an extensive financial analysis of the cost of a no packaged loan policy. The financial projections indicated that the College could replace loans with grants within its existing financial aid resources through a combination of actions, including reductions in the amount of merit aid and, most significantly, a reallocation of unrestricted endowment funds that were then being used to support a 0% interest institutional loan fund.

The financial collapse that caused the recent economic recession soon followed. As with most colleges and universities, the economic conditions of the past several years have placed significant pressure on the College’s operating budget. The College has worked to navigate through this period, which has included increasing our commitment to institutional financial aid at almost twice the rate of tuition increases. But we have had concerns about the sustainability of the No Packaged Loan policy, as we have focused on doing everything we can to ensure CMC remains accessible and affordable to all qualified students, regardless of need.

It is within this context that the Board has been engaged this year in a number of important discussions related to the costs and funding of a CMC education, and about the No Packaged Loan policy in particular. This discussion has also included valuable insight and analysis from the faculty, particularly from the faculty’s Admission and Financial Aid Committee (AFAC), who examined the effects of the No Packaged Loan policy on lower-income and minority applicants since the program’s inception five years ago. The Board weighed the AFAC’s findings in their decision and is appreciative of this research.

Through these discussions and careful analysis, the Board decided that, although the No Packaged Loan policy was important to preserve, if feasible, the College’s overarching priority should be to preserve and protect the College’s need-blind admission policies.

In making the decision to eliminate the No Packaged Loan policy, the Board reaffirmed several important commitments:

That the College is committed to providing access to all qualified students based on academic talent and not on financial need;

That the College is committed to securing and strengthening its need-blind admission and to meeting full-need policies by making fundraising for financial aid a priority;

That the College is committed to ensuring that packaged loan levels are reasonable and affordable;

That the College’s financial aid budget will not be reduced by this decision, and ongoing evaluation of the financial aid budget should take place during the next five years.

To help meet these goals, the Board authorized the administration to develop a plan for a targeted fundraising initiative that will focus on securing additional support for financial aid.

It seems probable that many colleges and universities across the country will soon be conducting similar evaluations of their financial aid policies and making changes. It is important for each institution to develop a strategy that assists students and their families to afford higher education with a program that is financially sustainable for the institution. I believe that is what we have done here.

Sincerely,

Pamela Gann
President

Liked what you read? I am available for hire.

Huh?

According to a new Zogby poll, 45% of Americans fear high levels of corruption if Hillary Clinton returns to office. What? I'm sorry, maybe I didn't hear correctly? As opposed to the low levels of corruption we are enjoying now? The tripling of paid lobbyists on K Street? The ethics 'reform' that means politicians are still enjoying lunches and paid vacations from those lobbyists? A representative soliciting a teen page for sex? No-bid contracts offered to the vice president's old company? Leaking the name of a CIA operative to exact revenge on her truth-telling husband? Congressional votes in exchange for pork/lobby money? Who is being kidded? Bill Clinton's presidency was a little before my time. But getting blown by a White House intern does not hurt the country, or its finances, or take money from its tax payers. I cannot believe the Clintons had scandals to match these. I mean maybe there will be corruption under Hillary Clinton but can it possibly top the amount now? Will there be less corruption under McCain or Obama or Edwards?

Liked what you read? I am available for hire.

Please, Put More Personal Information On The Internets

Google Maps today released a feature called "My Maps," where you can personalize Google Maps and add your favorite places to the map. This is pretty cool, except when you realize how easy we are making things for stalkers. That said, I can't wait for my favorite young, nubile, innocent female friends to place the location of their house on the Internet for everyone to see.

Liked what you read? I am available for hire.

We Are Less Violent Than Ever

From Steven Pinker:
Cruelty as entertainment, human sacrifice to indulge superstition, slavery as a labor-saving device, conquest as the mission statement of government, genocide as a means of acquiring real estate, torture and mutilation as routine punishment, the death penalty for misdemeanors and differences of opinion, assassination as the mechanism of political succession, rape as the spoils of war, pogroms as outlets for frustration, homicide as the major form of conflict resolution—all were unexceptionable features of life for most of human history. But, today, they are rare to nonexistent in the West, far less common elsewhere than they used to be, concealed when they do occur, and widely condemned when they are brought to light.

Liked what you read? I am available for hire.