This time I failed... or succeed? It's up to you to decide.
I failed because I couldn't shrink the source code to less than 1.1KB
(it's 1159 bytes with #includes).
At the same time I succeed with implementation of all the features I
wanted:
- Hit points and death
- Control with num pad (1-9)
- Randomly generated maps, fully traversable levels
- Multi-levels with stairs down
- Items to collect (med-packs)
- Monsters to kill
- Monster corpses
- Stronger monsters on deeper levels
- Experience (gold) for killing monsters
- Monsters chasing player
- Field of view (Fanfares!)
Not bad for 1.1KB of source code?
Windows exe + screenshot:
http://www.alamak0ta.republika.pl/furhunter.html
Source code:
#include "curses.h"
#include <string>
#include <time.h>
#define S 40
#define B rand()%S
#define L M[x][y]
#define H(a,b)for(a=0;a<b;++a)
#define F H(x,S)
#define G H(y,S)
#define T H(k,3)H(l,3)
#define O N[x][y]
#define Q O=L;if(L==35)goto
Z;if(L>96){if(abs(x-f)<2&&abs(y-i)<2)h--,L-=32;else M[a][b]=L-32,L=46;}
int
c,e,x,y,k,l,f,i,h=20,g,d,M[S][S],N[S][S],v,w,a,b,nx,ny;char*t="789456123+><.`";float
m,n;
void main(){srand(time(0));initscr();resize_term(S,60);j:d++;F G
L=35;M[20][20]=46;
H(nx,S*S*S){f=x,i=y,x=B,y=B,a=0;T
if(M[x+k-1][y+l-1]!=35)a++;if(a>0&&a<4&&x&&y&&x!=39&&y!=39)
L=B?46:B/10?97+B/(10-d):43;}L=62,x=f,y=i,L=60;for(;;){F G O=32;F G
{v=f,w=i,k=x-v,l=y-w,c=x,e=y;
if(abs(k)>abs(l)){m=l/(float)k,n=w-m*v,k=k<0?-1:1;while(v!=c){a=v;b=ny;x=v+=k;y=ny=(m*v+n);Q}}
else
if(l){m=k/(float)l,n=v-m*w,l=l<0?-1:1;while(w!=e){a=nx;b=w;y=w+=l;x=nx=(m*w+n);Q}}Z:x=c,y=e;}
F G if (L>64 && L<91)L+=32;F G
mvaddch(y,x,O);mvaddch(i,f,64);mvprintw(20,50,"H%d $%d L%d ",h,g,d);
refresh();x=getch();if(h<0)return;T if(x==t[k+l*3])goto
q;q:if(k*l<9){x=f+k-1;y=i+l-1;if(L>='a')
{L--;if(L==96)g++;}if(strchr(t,L))f=x,i=y;if(L==62)goto j;if
(L==43)h+=5,L=46;}}}
http://www.alamak0ta.republika.pl/furhunter.html
Two fixes - source code shrinked to 1141 bytes + new FOV feature.
Now you see the whole maze out of FOV (game is not so 'dark').
regards,
Jakub
T.
int S=40; would save you 4 bytes!! Infact it doesn't even need a
newline character!
Anyway still impressive. Good Job.
It would save, but S is also used for arrays size (level, fov) and
constant is expected there.
I think FOV calculation could be shrinked and maybe I'd get the magic
1024 bytes :) With a few more optimizations I got 1128 bytes.
New version is here:
http://www.alamak0ta.republika.pl/furhunter.html
> Anyway still impressive. Good Job.
Thanks :)
regards,
Jakub
Even more optimizations and I have 1086 bytes (1.06KB).
62 more to cut :)
#include "curses.h"
#include <string>
#include <time.h>
#define S 40
#define B rand()%S
#define L M[x][y]
#define H(a,b)for(a=0;a<b;++a)
#define F H(x,S)H(y,S)
#define T H(k,3)H(l,3)
#define O N[x][y]
#define Q O=L;if(L==35)goto
Z;if(L>96){if(abs(x-f)<2&&abs(y-i)<2)h--,L-=32;else M[a][b]=L-32,L=46;}
int
c,e,x,y,k,l,f,i,h=20,g,d,M[S][S],N[S][S],v,w,a,b,r,s;char*t="789456123+><.`";float
m,n,o,p;void main(){srand(time(0));initscr();resize_term(S,60);j:d++;F
L=35;M[20][20]=46;H(r,S*S*S){f=x,i=y,x=B,y=B,a=0;T
if(M[x+k-1][y+l-1]!=35)a++;if(a>0&&a<4&&x&&y&&x!=39&&y!=39)L=B?46:B/10?97+B/(9-d):43;}L=62,x=f,y=i,L=60;for(;;){F
O=L==35?37:32;F{v=f,w=i,c=x,e=y;if(abs(o=x-v)>abs(p=y-w)){m=p/o,n=w-m*v;while(v!=c){a=v,b=s,x=v+=o<0?-1:1,y=s=m*v+n;Q}}else{m=o/p,n=v-m*w;while(w!=e){a=r,b=w,y=w+=p<0?-1:1,x=r=m*w+n;Q}}Z:x=c,y=e;}F
if(L>64&&L<91)L+=32;F
mvaddch(y,x,y==i&&x==f?64:O);refresh();printf("H%d $%d
L%d",h,g,d);x=getch();if(h<1)break;T if(x==t[k+l*3])goto
q;q:if(k*l<9){x=f+k-1;y=i+l-1;if(L>=97){L--;if(L==96)g++;}if(strchr(t,L))f=x,i=y;if(L==62)goto
j;if(L==43)h+=5,L=46;}}}
regards,
Jakub
S*S*S = S^3 ;)
2 less bytes ?
T.
L>=97 = L>96
1 less byte.
59 to go.
--
Soyweiser
> S*S*S = S^3 ;)
>
> 2 less bytes ?
>
That's the XOR operator.
S*S*S ≠ S^3
--
OU
It's xor in C.
By the way - I DID IT!!! It's 1024 bytes now! :)
Tricks used:
- Map is smaller - 24x24 - no need to resize term
- Terminal switched to raw mode - no need to refresh
- Removed need for strchr by removing useless stairs up
- Map generator initialization changed
- Some smaller fixes
Removed RNG initialization by time() (unfortunatelly) - need to press
random key at start to init RNG. With it I had to #include <time.h>
that was too long.
I had 3 bytes left to format stats string into full "HP" ;)
http://www.alamak0ta.republika.pl/furhunter.html
regards,
Jakub
It looks like C but since you include <string> instead of <string.h>
I'll try to compile it as both C and C++:
:~$ gcc -ansi -pedantic -Wall -lncurses furhunter.c
furhunter.c:2:18: error: string: No such file or directory
furhunter.c:10: warning: return type of ‘main’ is not ‘int’
furhunter.c: In function ‘main’:
furhunter.c:10: warning: implicit declaration of function ‘srand’
furhunter.c:10: warning: implicit declaration of function ‘rand’
furhunter.c:10: warning: implicit declaration of function ‘abs’
:~$ g++ -ansi -pedantic -Wall -lncurses furhunter.cpp
furhunter.cpp:10: warning: deprecated conversion from string constant to
‘char*’
furhunter.cpp:10: error: ‘::main’ must return ‘int’
The warnings can be ignored, but there are errors prohibiting proper
compilation.
--
OU
Remove "#define S 24" for direct usage of "24" all around the code; 4
more bytes for you!
Can't you use "x=y=B" instead of "x=B,y=B" in C? that would be 2 more
bytes
--
Slashie
Thanks, I can do both of it :)
regards,
Jakub
what if you remove -ansi -pedantic ?
I have no GCC compiler near me to test it.
regards,
Jakub
This won't work. B is rand()%24 and then x=y instead of two different
random values.
regards,
Jakub
It's C++. <string> is shorter than <string.h> ;)
>> ~$ g++ -ansi -pedantic -Wall -lncurses furhunter.cpp
> furhunter.cpp:10: warning: deprecated conversion from string constant to
> 'char*'
> furhunter.cpp:10: error: 'main' must return 'int'
Fixed. main returns int.
I have also limited game to 7 levels as a goal to reach.
Try to get as much $ as you can on these 7 levels without beeing
killed.
Source code (C++):
#include "curses.h"
#include <string>
#define B rand()%24
#define L M[x][y]
#define H(a,b)for(a=0;a<b;++a)
#define F H(x,24)H(y,24)
#define T H(k,3)H(l,3)
#define Q N[x][y]=L;if(L==35)goto
Z;if(L>96){if(abs(x-f)<2&&abs(y-i)<2)h--,L-=32;else M[a][b]=L-32,L=46;}
int
c,e,x,y,k,l,f,i,h=20,g,d,M[24][24],N[24][24],v,w,a,b,r,s;char*t="789456123";float
m,n,o,p;int main(){initscr();raw();srand(getch());j:d++;F
L=35;M[9][9]=46;H(r,9999){f=x,i=y,x=B,y=B,a=0;T
if(M[x+k-1][y+l-1]!=35)a++;if(a>0&&a<4&&x&&y&&x!=23&&y!=23)L=B?46:B/9?97+B/(9-d):43;}L=62;for(;;){F
N[x][y]=L==35?37:32;F{v=f,w=i,c=x,e=y;if(abs(o=x-v)>abs(p=y-w)){m=p/o,n=w-m*v;while(v!=c){a=v,b=s,x=v+=o<0?-1:1,y=s=m*v+n;Q}}else{m=o/p,n=v-m*w;while(w!=e){a=r,b=w,y=w+=p<0?-1:1,x=r=m*w+n;Q}}Z:x=c,y=e;}F
if(L>64&&L<91)L+=32;F mvaddch(y,x,y==i&&x==f?64:N[x][y]);printf(" HP%d
$%d L%d ",h,g,d);x=getch();if(h<1||d>7)return 0;T if(x==t[k+l*3])goto
q;q:if(k*l<9){x=f+k-1;y=i+l-1;if(L>96){L--;if(L==96)g++;}else
if(L!=35)f=x,i=y;if(L==62)goto j;if(L==43)h+=5,L=46;}}}
regards,
Jakub
That won't stop it bitching about main() not returning int.
The C++ Standard *requires* main() to return int. It is not valid C++
code if main() either does not return int, or has no return type
specified (there are no implicit return types in C++).
--
\_\/_/ turbulence is certainty turbulence is friction between you and me
\ / every time we try to impose order we create chaos
\/ -- Killing Joke, "Mathematics of Chaos"
From C++ Standard (3.6.1.2):
An implementation shall not predefine the main function. This function
shall not be overloaded. It shall
have a return type of type int, but otherwise its type is
implementation-defined.
Also (3.6.1.5):
A return statement in main has the effect of leaving the main function
(destroying any objects with automatic
storage duration) and calling exit with the return value as the
argument. If control reaches the end
of main without encountering a return statement, the effect is that of
executing
return 0;
regards,
Jakub
That's what I said.
(I don't have a copy of the ISO C++ standard to hand, because last time
I looked, ISO wanted me to pay CHF340 for the PDF.)
Well... "otherwise its type is implementation-defined" and many
compilers support "void main()".
The second paragraph "If control reaches the end
of main without encountering a return statement, the effect is that of
executing return 0;" can be useful when writing 1KB roguelike.
regards,
Jakub
That's not what the text says. It says:
"It shall have a return type of type int," <-- this is non-optional.
"but otherwise..." <-- everything but the return type is optional.
Compilers that support void main() are not standards-compliant; this
only becomes meaningful when you move to a compiler that _is_.
> The second paragraph "If control reaches the end
> of main without encountering a return statement, the effect is that of
> executing return 0;" can be useful when writing 1KB roguelike.
That involves omitting the return statement entirely, moot here.
--
Derek
Game info and change log: http://sporkhack.com
Beta Server: telnet://sporkhack.com
IRC: irc.freenode.net, #sporkhack
Beware: in some places, I may be wrong about the precedence of the C
operators.
Sometimes one can do:
#define D define
if(L>96){if(abs(x-f)<2&&abs(y-i)<2)h--,L-=32;else M[a][b]=L-32,L=46;}
L>96&&(abs(x-f)<2&abs(y-i)<2&&h--,L-=32)||M[a][b]=L-32,L=46;
Moving the }} after each Q into the macro is possible but evil
ITYM "int main"
if(L==43)h+=5,L=46;
L^43?0:h+=5,L=46;
if(M[x+k-1][y+l-1]!=35)a++;
M[x+k-1][y+l-1]^35&&a++;
a=0;T if(M[x+k-1][y+l-1]!=35)a++;
if(a>0&&a<4&&x&&y&&x!=39&&y!=39)L=B?46:B/10?97+B/(9-d):43;
a=2;T M[x+k-1][y+l-1]^35||a--;
a*a<2&&x*y&&x^39&&y^39&&L=B?46:B/10?97+B/(9-d):43;
(If precedence permits, the condition can become:)
a*a<2&&x*y*x^39*y^39
O=L==35?37:32;
O=L^35?32:37;
v!=c ... w!=e
v^c ... w^e
F if(L>64&&L<91)L+=32
F L>64&L<91&&L+=32
y==i&&x==f?64:O
y^i+x^f?O:64 /* Assuming that i and f are positive */
I think curses's getch() does a refresh for you; maybe that would help
here? OTOH, refresh might be needed before the printf().
T if(x==t[k+l*3])goto q;q:if(k*l<9)
Aren't k and l always 0, 1, or 2 at the second condition?
Also, you can break instead of using a goto+label.
if(L>=97){L--;if(L==96)g++;}
L>96&&--L>96||g++;
if(strchr(t,L))f=x,i=y;
strchr(t,L)&&f=x,i=y;
if(L==43)h+=5,L=46;
L^43||h+=5,L=46;
HTH. I think it cuts 70 or so characters off.
--
Simon Richard Clarkstone:
s.r.cl?rkst?n?@dunelm.org.uk / s?m?n_cl?rkst?n?@yahoo.co.uk
| My half-daughter went to the GMH riots |
| But all I got was this stupid ¥-shirt. |
When they support void main(), they are not in compliance with the C++
standard.
The type of a function consists of its return type and its arguments (if
any). The above-quoted text says that *standard-compliant* main() *shall
have a return type of int*, but that the *permitted arguments* of main()
shall be defined by the implementation.
>The second paragraph "If control reaches the end
>of main without encountering a return statement, the effect is that of
>executing return 0;" can be useful when writing 1KB roguelike.
Yes.
Thanks! Very nice optimizations!
With them I was able to put "proper" RNG initialization and to fix code
in many places. I could add other dungeon generator but IMHO program
should be stable first.
regards,
Jakub
Well... Not to start a new thread.
Here is my new tiny roguelike game "The Horde"
http://www.alamak0ta.republika.pl/horde.zip
This time I didn't play much with code shrinking and it's about 2KB
long. For sure it can be less than 1KB if anyone wants to improve size
:)
THE STORY
"You play Chauncey, one of King Winthrop the Good's humble servants.
During a meal at which you are serving food, the king begins choking on
a bit of turkey.
The other people at the table are so engrossed in one of Kronus
Maelor("the Evil High Chancellor")'s stories, that they don't notice
the king's predicament. But you, realizing the magnitude of the
situation, perform a royal Heimlich maneuver on his highness, saving
his life.
Although Maelor wishes you imprisoned for attacking the king, King
Winthrop has a better grasp on the situation and rewards your valor by
knighting you "Sir Chauncey" and giving you a small tract of land known
as the Shimto Plains.
While vast tracts of land are nice, these particular tracts of land are
infested with "The Horde". Luckily, as part of your reward for saving
his life, the good king has given you his mighty hordling-crushing
sword, Grimthwacker with which to thwart the advance of the foul
beasts."
CONTROLS
1-9 Move
5 - Place/improve fence ($1), expensive fence can kill monsters.
c - Place cow ($20)
t - Wait for next season/horde to come
GOAL
Your goal is to reach 2000$ to pay tax to the Evil High Chancellor.
At the beginning of each season you get $10 per cow.
You loose when you are out of money.
The game is quite fun and the goal is reachable :)
regards,
Jakub