The official Viridian Games site now has proper pages for Sandworm! and Castle Adventure, as well as updated source code and executables for both those games. So if you tried to play them earlier and had trouble, head over there and give it another shot!
You Should Call Me Mario
Because I just got one-upped. I thought writing a game in one page of source was good, but the last time I tried to write an RPG in one page of source, I concluded that it probably wasn’t possible.
But now the Temple of the Roguelike is having an ongoing competition for people to write Roguelikes in one kilobyte of source; that’s less than one-fourth the size of a one-page game. Most of the entries so far have been pretty simplistic, but now a true challenger has emerged, called A Journey to Hell.
Here’s the complete source code (compiled on Linux using GCC and curses):
#include <curses.h>
#define W(c)while(c)
#define F(v,h)for(v=0;v<h;v++)
#define D(i)F(y,S)F(x,T){i;}
#define G d[x][y]
#define M d[x+f][y+g]
#define Z(n)n<0?-1:(n>0?1:0)
#define r rand()
int x,y,S=24,T=60,c,d[90][90],L=1,b,k[]=L"$`*$.@?$?>",f,g,z,h,i,l=9,p=20,e=46,
v,u=96;m(f,g){G-64?M-64||(l-=G-u)>0||(M=e):(M^62||L++&&(L^7?N():(G=e)),M^u||(p
+=L,M=e),M^42||(l=p,M=e),M^36||(v+=x%9*L+L,M=e),M^63||(M=r%6?k[r&1]:97+L),M>u&
&--M);M-e?M-35||(g&&m(f,0),f&&m(0,g)):(M=G|256,G=e);}E(t){W(d[x=r%T][y=r%S]-e)
;G=t;}N(){D(G=35)x=y=9;F(b,S){f=r%58+1;g=r%22+1;W(f^x||g^y){G=e,r&1?(x+=Z(f-x)
):(y+=Z(g-y));}}F(b,10)E(k[b]);F(b,L*2)E(98+r%L);}main(){srand(initscr());
raw();start_color();F(c,8)init_pair(c,c,0);N();W(c-81){D(G-64||(f=x,g=y))
clear();D(b=1;W(G^k[b]&&b<7)b++;h=x;i=y;c=0;W(c++<u)(d[f+(z=(h-f)*c*.01)][g+(z
=(i-g)*c*.01)]-35)||(b=0);b&&mvaddch(y,x,G|COLOR_PAIR(b)))mvprintw(S,0,"HP:%d/
%d L:%d $%d %s",l,p,L,v,L^7?"":"WIN");c=getch();D(G>u&&G<123&&m(Z(f-x),Z(g-y))
)D(c<49||c>57||G-64||m((c-1)%3-1,1-(c-49)/3))D(G&=255)}endwin();}
Here’s a screenshot.
In this screenshot, the purple ‘@’ is the player (of course). The yellow ‘$’ represents money on the ground. The blue-green ‘?’ represents a chest. The white ‘b’ is a monster; the letter of the monster represents how hard it is to kill, from a (weakest) to z (flee in terror). The red quote mark represents a soul left behind by a monster I just killed; picking this up raises my maximum hit points.
Here’s what that 1K of source gets you:
* Guaranteed traversable levels
* Line-of-sight on the player
* Colored graphics
* Combat with monsters
* Ability to pick up gold and souls (dropped by defeated monsters; powers player up)
* Chests (which can contain gold, a monster or a soul)
* Ability to go down the stairs (generates a new level with tougher monsters)
* Win condition (beat the seventh level to win the game)
Of course his platform gives him a couple of advantages. My library to write colored graphics to the screen and poll the keyboard and mouse under Windows takes up almost 1K of code by itself; the curses library is much more space efficient. Also, GCC supports “default int” on both functions and parameters, meaning that Jakub can declare a function as “m(f,g)” and GCC will assume that ‘m’ is a function that takes two integer parameters, f and g, and returns an int itself. Visual C++ 2005 will not allow such shenanigans (though 2003 was not as strict).
Still, it’s an incredible amount of stuff for less code than it takes to initialize DirectX. I can only imagine what the author, Jakub Wasilewski, could do with the unending expanse a full page of source would provide him.
Collector – A One-Page Game
Bwahaha! The One-Page Game meme grows!
Casey Dunham has now written a one-page game called Collector. Collector is based on a famous arcade game just like my game Sandworm was. Which one? I’ll let you find out yourself 🙂
Casey said that he likes the one-page game format because he’s so busy as a student that he doesn’t really have time for anything more involved. Well done, Casey!
Childlike Wonder
I told you I had a surprise coming for you guys in the mail.
I actually managed to dig up a copy of the original article that inspired my series of One-Page Games. This article is from The Rainbow Magazine, August 1986 issue. It is over twenty years old.
There are two old magazines that I absolutely adore and will pick up copies anytime I can find them. They are The Rainbow and The Space Gamer. And the reason I like both of them is because of the attitude they conveyed.
The readers and editors of The Rainbow were all in a state of fascination – “Look at this! This is a computer we can own! How cool is that! I wonder what I can make it do…”
The readers and editors of The Space Gamer were the same way, amazed at the stories they could tell and the fun they could have with roleplaying.
In both magazines you get a sense of childlike wonder as people explored these previously unexplored continents – one in the mind and the other in silicon.
I miss that attitude. I haven’t seen it in a long, long time. It has been replaced by cynicism and curmudgeonism. And that makes me sad.
When Steve Jackson Games started their magazine Pyramid I was hoping to see a little of that attitude come back, but I was disappointed. The feel of Pyramid was slick and professional; the attitude was almost that of people telling an inside joke – yes, we love games, but we don’t…love-love games, because that would be really dorky. Especially since we’re all over thirty now.
That’s not to say that Pyramid wasn’t an excellent magazine – it is. I was a subscriber for over three years and enjoyed reading it very much. I’m just saying that the attitude is different.
And on the video gaming side…do you remember the first game that you absolutely obsessed over? The one you simply could not stop playing? The one you could barely drag yourself away from to go to school or work, and would instantly resume playing once you got home?
I’ve had several games like that. One of them was Civilization. Another was Doom. Tie Fighter was another. And so was Fallout.
When is the last time a game made you feel like that?
This is why I adored Oblivion so much – it was the first time in years that I felt I could just fall into a game and live there for a while, forgetting everything else (well, after the chilluns were in bed, of course). But before Oblivion I’d have to go back years, practically to the Golden Age, to find a game like that.
Is the games? Or is it me?
Have I lost my own childlike wonder? I’d like to think I haven’t…
Well, I love-love games. Truly. With all my heart. Yes, that makes me a geek. And a dork. And a knucklehead mcspazatron.
This is me. I am Gabe in this comic. Except that, you know, I’ve never actually put a game down my pants.
I still think it’s incredibly awesome that video games exist and I get to play them, and I hope I never lose that feeling. I also need to get better at game development so I can make more of my own games, in an effort to help other people feel the same way.
Sniff…I love you all, guys! I love you all!
One Page Game #3: Quad Force!
Well, not exactly one-page. Tom’s game turned out to be about 2.5 pages in length, but you will not believe how cool it is. It’s basically a little Zelda clone in 3D, with an overworld and four (count them, FOUR!) dungeons to explore, each one with its own tricks and traps.
Head on over to his site for the code and the executable. Be sure to read the readme, or you’ll probably have trouble figuring out what is going on.
One Page Game #2
Sigh…I must be crazy.
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.
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.
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…
New Game Development Articles
Well, new to me, anyway.
While I’ve been working on Sandworm (my One-Page Game) I’ve been learning more about the Windows console application functions. While these aren’t really hard to use, example code is always helpful and Microsoft provides very little. So I’d been searching both the internet and Google Groups for more information on the assumption that I can’t be the only person using this stuff. But I hadn’t really found anything.
So Sandworm was almost finished when a Google search on the WriteConsoleOutput() function pointed me to an article I really really wish I’d found before I started coding. The code this article provides is much better than what I’d written, so I’m incorporating it. And anyone else who wants to write a console application should read it. It even includes an ASCII table!
And Sol has written a set of tutorials on immediate-mode GUIs. If you’ve used his SDL tutorials you know he’s an excellent writer, and immediate-mode GUIs look better to me every day (you’ll recall I used one for my combat prototype).
A New Challenge: The One-Page Game
(Because I didn’t have enough projects going on at once…)
Basically, I’ve been working out of my book for about two weeks now, and I’ve written very little code. That bugs me. But I can’t really start on Planitia yet, can I?
So I began to think about doing a little game on the side in the meantime. A very little game – one that would scratch my game development itch but not take a bunch of time away from the book.
And then I remembered a little something I read in The Rainbow back in high school.
The Rainbow was a magazine for the Color Computer, which was Radio Shack’s competitor for the Apple II/Commodore 64/Timex Sinclair 1000 market. The Color Computer ran on a Motorola 6809 processor and came with 4k of RAM (later models came with 16k and there were expansion systems designed to bump it up to the “magic” 64k). It was hampered a bit by its weak display system, but overall it was a good little computer and lots of hobbyists used it for lots of different things.
And of course support magazines sprung up for it. The Rainbow was the biggest and most successful of these, and it was an old-school support magazine in that it included source listings that users could type in themselves. It covered just about every application you could put a computer to, which included games. Indeed, their annual game issue was always their biggest seller. During its heyday issues of The Rainbow ran over 300 pages and looked like phone books because of all the included source code.
I read a whole bunch of issues in high school. I borrowed them from a friend and thoroughly enjoyed them despite not having a Color Computer of my own. (Back then I would read anything about computers I could get my hands on.) And I recalled reading a small article in one issue that presented a version of Centipede a reader had written and submitted. This game was unique because the source code only took up a little more than one page. I remembered being very impressed when I saw that.
And I decided that writing a complete game in one page of source would be a unique challenge that wouldn’t take too much time away from learning DirectX.
So here comes a new challenger!
The One-Page Game
What does it mean to write a game in one page? Well, after trying it myself and having some of my friends try it, we’ve come up with a couple different interpretations.
Challenge 1: The True One-Page Game
To complete this challenge, write a game whose source is fully contained in one page of text that is eighty characters wide and sixty lines long. No external data files are permitted; this source file must contain everything your game needs to run. You may only use the base libraries that come with your compiler.
Of course, in order to complete this challenge, you’ll have to write source code that looks like this:
#include <windows.h> HANDLE h;SetCur(bool b){CONSOLE_CURSOR_INFO c = {1, b};SetConsoleCursorInfo(h, &c);}Init(){h = GetStdHandle(STD_OUTPUT_HANDLE);SetCur(false);}SetPos(int x, int y){COORD c = {x, y};SetConsoleCursorPosition(h,c);}SetColor(int f, int b){ SetConsoleTextAttribute(h,f|b);}Print(char* i){DWORD r;WriteConsole(h,i, strlen(i),&r,NULL);}
If this bothers you, you can try Challenge 2.
Challenge 2: The 4800-Byte Game
To complete this challenge, write a game whose source is fully contained in one file that does not exceed 4800 bytes in length. The actual file can be as long as you want it. While you may think you’re getting a little extra room with this method, I think with the extra whitespace it’s a wash, so this is mostly an aesthetic decision. Again, no data files or libraries except what came with your compiler.
And if that just isn’t enough room for you (or you abhor writing text-based games) you can try Challenge 3.
Challenge 3: The Two-File Game
To complete this challenge, write a game whose source is fully contained in two files, one header and one source, which are less than 4800 bytes each (9600 bytes total). Again, no data files or libraries except what comes with your compiler, but under Windows this includes DirectX and D3DX so this version opens up the option of using 3D rather than making a text-based game. Tom has already created an incredible-looking game that fits this challenge. Hopefully he’ll post it soon.
As for me, my game will fulfill Challenge 1. It’s going to be a Centipede clone in honor of that article I read in The Rainbow. And it would be done by now if I hadn’t spent so much time figuring out how to get the mouse to work with a console application…
November 2024 S M T W T F S 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30