One Page Game #2

Sigh…I must be crazy.

It's land!

RPGs are just so information dense; doing one in one page seems almost impossible without cutting practically every feature that makes an RPG an RPG. But as you can see, I’m thinking positive…this is basically a much more effective reworking of the terrain generation I started on with 40-Hour RPG #1 (which I failed to complete, if you’ll remember).

Basically, I wanted a algorithm that made maps that reminded me of this. I must use an algorithm, because I want the map to be 64×64 (I don’t think a smaller map will feel big enough to represent a world). If I tried to simply use a data file, I’d waste 4096 bytes just on the world. An algorithm that takes up fewer than 4096 bytes and can generate a random world would be a big win.

I now know what I was doing wrong with my first terrain generator. I walked the Path of Perlin and tried to do a heightfield. I can’t really blame myself; google “random terrain generation” and practically all you will get are articles about heightfields.

You know what? I frigging hate heightfields. For one thing, they are not an accurate representation of how terrain looks. They’re too smooth; too rolling. They say nothing about what type of terrain it is or what is on the terrain, just what elevation it is. And you will never, ever get a realistic-looking river out of a heightfield.

So I went back to a very early idea I had – that of seeding the map grid at regular intervals with terrain types and then having them grow and fight each other for terrain tiles. I effectively walked the Path of Conway instead. This worked much better and started giving me maps that I felt I might be able to use, but I still think there is room for improvement.

My current intention is to include both a world generation algorithm and a dungeon level generation algorithm. I will probably run both until I get results I like and hardcode those seeds so that the player gets known content; otherwise the odds are good that the game would be unfinishable.

I am going to do my best to get it into one page. Going the two-page route here seems like a copout, since I’m almost positive I could do an RPG in two pages. (Especially since Tom is already doing an RPG in two pages. Gosh, it sure would be nice if he’d update his blog with a couple screenshots of it.)

Let’s see what I can do in one.

Name That Game 5!

I’m sick as a dog, but I already got this screenshot together last week, so I can still do this:

Well, it's probably...

I have come to realize that I’m on a real RPG kick and I need to get off of it. I’ve no doubt Warren will identify this game in a microsecond.

Anyhoo, name and developer, please.

Name That Game 4!

First off, forgive me. My schedule has been off all week; I meant to post this on Tuesday. I just hope I remember to do my video blog on Saturday.

Honestly? I don’t know if this is a hard one or not. I’m just gonna throw it out there and see who gets it first.

Maybe it's...

Name and developer, as usual.

One-Page Game #1 – SANDWORM!

My first One-Page Game is complete, and I completed the most restrictive challenge – a game in one 80×60 page! And here it is!


#include <windows.h>
#include <vector>
using namespace std;HANDLE wh;HANDLE rh;CHAR_INFO bb[4000];COORD cBS={80,50};
COORD cPos = {0,0};SMALL_RECT wA = {0,0,79,49};bool running=true;int fc = 0;
struct u{int x;int y;int dx;int dy;int state;};vector<u> c;vector<u> mush;
vector<u> bullets;u s = {20, 49, 0, 0, 1};int score,lives,i,j;SetChar(int x,
int y,char c,int a){bb[(y*80)+x].Char.AsciiChar=c;bb[(y*80)+x].Attributes=a;}
void ResetC(){c.clear();u head={19,0,1,1,1};c.push_back(head);for(i=18;i>=0;--i)
{u b={i,0,0,0,0};c.push_back(b);}}SetPos(int x,int y){COORD c1={x,y};
SetConsoleCursorPosition(wh,c1);}MoveCP(){if(fc%2){for(i=19;i>-1;--i){int headx,
heady;if(c[i].state==1){int tx=c[i].x+c[i].dx;bool hm=false;for(j=0;j<
mush.size();++j){if(c[i].x+c[i].dx==mush[j].x&&c[i].y==mush[j].y&&mush[j].state)
hm=true;}if(tx>40||tx<0||hm){c[i].dx=-c[i].dx;int ty=c[i].y+c[i].dy;if(ty>49||
ty<0||(ty<39&&c[i].dy==-1)){c[i].dy=-c[i].dy;}c[i].y+=c[i].dy;}else{c[i].x+=
c[i].dx;}}else{c[i].x=c[i-1].x;c[i].y=c[i-1].y;}}}}MoveS(){bool ok=true;for(i=0;
i<mush.size();++i){if(s.dx==mush[i].x&&s.dy==mush[i].y)ok=false;}if(ok){s.x=
s.dx;s.y = s.dy;}}MoveB(){for(j=0;j<2;++j){for(i=0;i<20;++i){int mrx,mry;
if(c[i].state==1){mrx=c[i].dx;mry=c[i].dy;}if(bullets[j].x==c[i].x&&bullets[j].y
==c[i].y&&bullets[j].state!=0&&c[i].state!=-1){if(c[i].state==0)score+=10;else
score+=50;u m={c[i].x,c[i].y,0,0,1};mush.push_back(m);bullets[j].state=0;
c[i].state=-1;if(i!= 19){if(c[i+1].state!=-1){c[i+1].state=1;c[i+1].dx=mrx;
c[i+1].dy = mry;}}int lc=0;for(int z=0;z<20;++z){if(c[z].state!=-1)++lc;}
if(lc==0)ResetC();}}for(i=0;i<mush.size();++i){if(bullets[j].x==mush[i].x&&
bullets[j].y==mush[i].y&&bullets[j].state!=0&&mush[i].state==1){bullets[j].state
=0;mush[i].state=0;score+=3;}}if(bullets[j].state==1){--bullets[j].y;
if(bullets[j].y < 0)bullets[j].state=0;}}}Init(){wh=GetStdHandle(
STD_OUTPUT_HANDLE);rh=GetStdHandle(STD_INPUT_HANDLE);SMALL_RECT w={0,0,79,49};
SetConsoleWindowInfo(wh,TRUE,&w);COORD b={80,50};SetConsoleScreenBufferSize(wh,
b);}Input(){DWORD nE=0;DWORD nER=0;GetNumberOfConsoleInputEvents(rh,&nE);if(nE!=
0){INPUT_RECORD *eB=new INPUT_RECORD[nE];ReadConsoleInput(rh,eB,nE,&nER);for(i=
0;i<nER;++i){if (eB[i].EventType==KEY_EVENT){if(eB[i].Event.KeyEvent.
wVirtualKeyCode==VK_ESCAPE){running=false;}}else if(eB[i].EventType==
MOUSE_EVENT){s.dx=eB[i].Event.MouseEvent.dwMousePosition.X;if(s.dx>40)s.dx=40;
s.dy=eB[i].Event.MouseEvent.dwMousePosition.Y;if(s.dy<40)s.dy=40;if(eB[i].Event.
MouseEvent.dwButtonState&FROM_LEFT_1ST_BUTTON_PRESSED){for(int z=0;z<2;++z){
if(bullets[z].state == 0){bullets[z].x=s.x;bullets[z].y=s.y-1;bullets[z].state
=1;break;}}}}}delete eB;}}ResetM(){mush.clear();for(i=0;i<100;++i){u m={rand()
%40,(rand()%46)+1,0,0,1};mush.push_back(m);}}int main(int argc,char* argv[])
{Init();ResetC();ResetM();u b1={ -1, -1, 0, 0, 0};bullets.push_back(b1);bullets.
push_back(b1);score=0;lives=3;while(running){ZeroMemory(bb,4000*sizeof(
CHAR_INFO));Input();MoveCP();MoveS();MoveB();for(i=0;i<50;++i)SetChar(41,i,219,
15);SetChar(s.x,s.y,127,15);for(i=0;i<20;++i){if(c[i].state==1){SetChar(c[i].x,
c[i].y,1,2);}else if(c[i].state==0){SetChar(c[i].x,c[i].y,15,2);}}for(i=0;i<
mush.size();++i){if(mush[i].state){SetChar(mush[i].x,mush[i].y,6,14);}}for(i=0;
i<2;++i){if(bullets[i].state)SetChar(bullets[i].x,bullets[i].y,24,15);}
WriteConsoleOutput(wh,bb,cBS,cPos,&wA);bool ll=false;for(i=0;i<c.size();++i){
if(c[i].state!=-1&&c[i].x==s.x&&c[i].y==s.y){--lives;ll=true;ResetC();}}int mc
=0;for(i=0;i<mush.size();++i)if(mush[i].state)++mc;if(mc<50){int nm=0;do{nm=
rand()%mush.size();}while(mush[nm].state);mush[nm].state=1;}SetPos(45,0);printf(
"SANDWORM!");SetPos(45,1);printf("Score: %d",score);SetPos(45,2);printf(
"Lives: %d",lives);if(!lives){SetPos(16,20);printf("GAME OVER");Sleep(5000);
score=0;lives=3;ResetM();}else if(ll)Sleep(1000);else Sleep(17);++fc;}return 0;}

Create a new console application project in Visual Studio, paste that into the source file, and compile it. If you have trouble compiling the file, it might be easier to debug if you had a cleaner version – so here’s one.

Or, if that’s too much trouble, you can just download the executable.

The game is a simplified version of the classic arcade game Centipede. You control your shooter with the mouse and fire with the left mouse button. Shooting the sandworm creates a new rock and splits the sandworm into two separate parts. Breaking a rock gets you three points, hitting a sandworm segment gets you ten points, and hitting a sandworm head gets you fifty points. You can have up to two shots on the screen at the same time. If you manage to kill the entire sandworm, a new one respawns. If any part of the sandworm hits you, you lose a life. Lose all your lives and it’s game over, but a new game will start in a few seconds automatically. Press ESC on your keyboard to exit the game. Press ALT + ENTER to play in fullscreen mode.

Lots of heads!

Here I’ve chopped the sandworm up into sushi…I’m probably going to regret that in a few minutes.

I was quite nervous about coming in under the limit because of the incredibly long function and constant names Windows insists on using. I was surprised when I did the final compress to see that I had a full eight lines free, more than enough space to put in another feature like the spider or the flea! But this will do for a first version.

Needless to say, the code is filthy. You may have problems on other platforms with the fact that I don’t declare return values for a lot of my functions (VS 2003 supplies int automatically) and use variables without initializing them first. And if you’re not on Windows…well, good luck 🙂

So, challenge successfully accomplished! I just wish Tom would get off his duff and complete his two-page 3D RPG…you will NOT believe your eyes when you see it.

For my next challenge, I will almost certainly attempt an RPG in one page of source. But it’s not like I’m obsessed with RPGs or anything…

Comments, Yet Again

Certain people were having trouble with the comment system not remembering them and therefore having to register every time they left a comment. I went ahead and turned registration off. This will probably cause an increase in comment spam, which I will fight as best I can.

Lee’s Wedding

This past weekend, one of my best friends got married.

Lee Gibson and I met at Origin Systems, where we were both testers. He can tell you anything you want to know about Wing Commander IV, having tested the PC, Mac and PC-DVD versions. He’s also a damn good Quake player. Some of my happiest memories of Origin are of me, him and Anthony Castoro playing multiplayer in the House of Cthon.

After we were both let go from Origin, we both found phone tech support jobs elsewhere. (I suppose I should thank Origin for giving me that useful skill, because it meant that I didn’t stay out of a job for long like I would have if all I had on my resume was “tester”.) He suggested I come over to where he was at Dun & Bradstreet, as it paid far better than MaxServ, which is where I ended up.

Thus we stayed in close contact and became even better friends. Finally he left Austin to go back to school and finally finish his aeronautical engineering degree (why yes, he is a rocket scientist). We kept in close touch using various IM programs.

Now, one thing I could never understand was why the poor guy couldn’t get married. I knew him personally, I knew he was sharp, funny and had a bright future, so it just astounded me when he would tell me of yet another flaky girl that he’d split up with.

Finally he met a nice girl named Barbara. From Portland, Oregon. Of course, given Lee’s history, I’m not surprised that he didn’t let that stop him. They continued their relationship using various IM programs.

And one day Lee went up there to see her and didn’t come back. Until it was time for the wedding, of course.

Now, Top Spin 2 had just wrapped the day before, and we’d been crunching rather a lot to get it done. And it’s a four-hour drive from Austin to Plano. I honestly wouldn’t have done it for anyone else.

But I’m so glad I went.

The wedding took place in Lee’s family church, the Custer Road United Methodist Church. The church itself was beautiful, with one of the most amazing stained-glass windows I’ve ever seen. The ceremony was absolutely wonderful – it served tradition well while still having a few laughs to break up the tension (at one point a bridesmaid and a groomsman went skipping down the ailse together).

One thing I noticed was that Barbara’s dress was white with a pattern of red roses on it in red thread. Lee was wearing a white vest with the same red rose pattern on it.

The pastor did one thing I’ve never seen before. At one point he made all of us, the entire congregation, stand up and pledge that we would do everything in our power to help Lee and Barbara in their journey through married life. I gladly said, “I will”.

And I finally saw my friend put the single part of his life behind him, and begin the part where he becomes a family man. I myself look back on the person I was when I was single and I don’t really recognize him. I wasn’t really me until I married Jamie.

And when we got to the reception, the wedding cake was white with that same red rose pattern on it in red frosting!