Format and other fixes in 2020/ferguson1

Fixed formatting in the markdown files. I did not check for typos due to
the fact I wrote so much (which I'm only sorry about in the sense that
it's too much to go through for almost everyone and this might make some
people miss useful/interesting things. If I had 'more "time" ' maybe I
could shorten it but I don't).

Renamed crazy.log to crazy.log.md. Updated Makefile for the DATA
variable.

Corrected file names in some cases.

Fixed formatting in man page.

There is one other change I have in 2020/ferguson1 but it's also needed
in 2020/ferguson2 so I'll do that at the same time.
This commit is contained in:
Cody Boone Ferguson committed 2023-04-16 10:09:33 -07:00
1 parent a7e018d5e8
commit 49a6a4074d
11 files changed
+894 -732

No files matched your search

+30 -14
View File
@@ -1,10 +1,10 @@
In order to successfully compile this you will need a C compiler that supports
C99+ and ncurses; note that for some platforms (e.g. Red Hat based Linux) you
will need two different packages: the library itself but also the dev package
that has the header files etc. If you use the Makefile obviously you need `make'
that has the header files etc. If you use the Makefile obviously you need `make`
too.
In gameplay.md I show how to easily customise the colours, set up
In [gameplay.md](gameplay.md) I show how to easily customise the colours, set up
different gameplay modes and various other things. The troubleshooting.md
and terminals.md as well as bugs.md include a variety of information
should you run into trouble.
@@ -33,7 +33,9 @@ I imagine that it works for 32-bit systems but I do not know for sure.
You should be able to get the compiler with:
sudo xcode-select --install
```sh
sudo xcode-select --install
```
You don't need Xcode itself and it appears that the command line tools (as above
should install) already has ncurses so there shouldn't be anything else that
@@ -51,10 +53,15 @@ you want to use the Makefile) to compile and link this entry. With Fedora more
recent versions there's the `dnf` package manager but I cannot help with any of
the GUI installers since I don't use them. Something like:
sudo dnf install ncurses-devel ncurses make gcc
```sh
sudo dnf install ncurses-devel ncurses make gcc
```
Or
# dnf install ncurses-devel ncurses make gcc
or
```sh
# dnf install ncurses-devel ncurses make gcc
```
# Portability: CentOS
@@ -62,20 +69,29 @@ Just like Fedora you will need `gcc`, `ncurses`, `ncurses-devel` (and if you
want to use the Makefile `make`). CentOS uses `yum` and CentOS 8 uses `dnf`.
So:
sudo yum install ncurses-devel ncurses make gcc
```sh
sudo yum install ncurses-devel ncurses make gcc
```
Or
# yum install ncurses-devel ncurses make gcc
or
Or
sudo dnf install ncurses-devel ncurses make gcc
```sh
# yum install ncurses-devel ncurses make gcc
```
Or
or
# dnf install ncurses-devel ncurses make gcc
```sh
sudo dnf install ncurses-devel ncurses make gcc
```
or
You can also use Clang to compile.
```sh
# dnf install ncurses-devel ncurses make gcc
```
You can also use clang to compile (which is what gcc actually is under macOS).
# Compilation notes
+170 -138
View File
@@ -1,4 +1,11 @@
## Warning / caveat about this document
# About this document
In this document I provide information to the herpetologist hackers to help them
modify certain things and add features for their studying of snakes. Or if
you prefer you can be the snake studying the herpetologist. Whichever character
you prefer to play doesn't really matter.
# Warning / caveat about this document
WARNING: There are some things cited in this document that are no longer
correct, might be misleading or otherwise not clear. This is both intentional
@@ -7,21 +14,17 @@ something specific. Put another way just because the table says something
doesn't mean it's the whole truth: no misleading comments but misleading
document that talks about some parts of the code!
In this document I provide information to the herpetologist hackers to help them
to modify certain things and add features for their studying of snakes. Or if
you prefer you can be the snake studying the herpetologist. Whichever character
you prefer to play doesn't really matter.
Please do not assume that just because something is in this file means that it's
complete or will work without any changes. As I have changed the game over
development stages some features have been dramatically changed and some of
these ideas could be incompatible now or otherwise impractical. Also you might
find in fact that some of these leave more questions than answers (e.g. some
require making decisions for specific problems one might encounter)!
complete or will work without any changes (see above). As I have changed the
game over development stages some features have been dramatically changed and
some of these ideas could be incompatible now or otherwise impractical. Also you
might find in fact that some of these leave more questions than answers (e.g.
some require making decisions for specific problems one might encounter)!
With that said here's the C sibilancy (the herpetologist programmer's and
snake's syllabary) for all snake and herpetologist programmers alike.
# Modifying and adding additional features to the code
* Modifications
@@ -61,14 +64,14 @@ snake's syllabary) for all snake and herpetologist programmers alike.
- Max times the bug evades before game over
* MODIFICATIONS
# Modifications
Here I include information on modifying the game in various ways depending on
the preference of the herpetologist (or if you prefer you can be the snake
studying the herpetologist).
# Modifying the colours
## Modifying the colours
As I already said I provided a script that prompts you for colours and then
compiles the game with those colours (default if invalid input).
@@ -82,78 +85,88 @@ and background.
To change the colours use the snake-colours.sh or play.sh scripts. Hit enter for
defaults if you don't want to change the colour.
# Background colour of the game field
## Background colour of the game field
I prefer black backgrounds but this is the basic idea behind changing it. You
initialise a pair (make it 5 so as to not interfere with the script
customisability) and then you use the bkgd() function with the colour pair.
Something like this:
init_pair(5, COLOR_BLACK, COLOR_WHITE);
bkgd(COLOR_PAIR(5));
```c
init_pair(5, COLOR_BLACK, COLOR_WHITE);
bkgd(COLOR_PAIR(5));
```
I played with this after winning and I came across some problems with colours
but that changes the background of the program anyway.
The standard colour identifiers are:
COLOR_BLACK
COLOR_RED
COLOR_GREEN
COLOR_YELLOW
COLOR_BLUE
COLOR_MAGENTA
COLOR_CYAN
COLOR_WHITE
```c
COLOR_BLACK
COLOR_RED
COLOR_GREEN
COLOR_YELLOW
COLOR_BLUE
COLOR_MAGENTA
COLOR_CYAN
COLOR_WHITE
```
See Linux Journal for more information:
https://www.linuxjournal.com/content/programming-color-ncurses for more details).
See Linux Journal for more information article
<https://www.linuxjournal.com/content/programming-color-ncurses> for more
details.
# Modifying the characters for the wall, bug and snake
## Modifying the characters for the wall, bug and snake
For the wall, bug and snake body characters you can replace the following in the
source code (include the single quotes) respectively:
source code (**include the single quotes!**) respectively:
'#'
```
'#'
'*'
'*'
'o'
'o'
```
# Modifying input keys
## Modifying input keys
Let's say you don't want to use arrow keys or you don't have arrow keys. Instead
you might want the keys 'l', 'r', 'u' and 'd' for the directions. Find the case
you might want the keys `l`, `r`, `u` and `d` for the directions. Find the case
statements:
KEY_LEFT
KEY_RIGHT
KEY_UP
KEY_DOWN
```c
case KEY_LEFT:
case KEY_RIGHT:
case KEY_UP:
case KEY_DOWN:
```
Replace KEY_LEFT with:
Replace `KEY_LEFT` with:
'l'
```c
'l'
```
(Yes the single quotes matter)
And do the same thing for the other directions too. If you want more than one
and do the same thing for the other directions too. If you want more than one
key e.g. the arrow keys and the letters just add another case statement e.g. for
left it might be:
case KEY_LEFT: case 'l':
```c
case KEY_LEFT: case 'l':
```
Yes the single quotes matter. You can do similar for the other input keys.
Please note that modifier keys like ctrl aren't detectable or at least I'm
unaware of how to do so.
You can do similar for the other input keys. Please note that modifier keys like
ctrl aren't detectable or at least I'm unaware of how to do so.
## Modifying the status/score line (table of variables/stats below)
# Modifying the status/score line (table of variables/stats below)
* SPOILER WARNING
### SPOILER WARNING
Below I give a table of some variables/expressions along with the data types and
their respective format specifiers of the data. Originally it was a lot more
@@ -167,11 +180,15 @@ different sets. Please also be aware that if you don't want any information you
must set it to the below at the very least; defining it without a value is not
enough (it amounts to the parameters to a printf call):
#define J " " );
```c
#define J " " );
```
If you don't care about brief displays of 'o' at 0,0 you can just have:
#define J "");
```c
#define J "");
```
That's the bare minimum. Notice also that it's been a long while since I've
tested this part but I think this is right.
@@ -191,7 +208,9 @@ coordinates of where the snake is plus the other things that are there (a number
of which were added over time). Obviously this would take many bug sized bytes
(bites) even once let alone twice! Thus I made it into a define:
#define J Z(X:%d/%d Y:%d/%d S:%zu/%zu B:%zu\n), Y, y[-2], X,*y,A,g, c);
```c
#define J Z(X:%d/%d Y:%d/%d S:%zu/%zu B:%zu\n), Y, y[-2], X,*y,A,g, c);
```
With the table below you can show a variety of other variables/statistics.
@@ -199,7 +218,7 @@ There's another reason for changing this line though: depending on the terminal
width the line could cover the top wall; this can even happen only part of the
game because the line length is not constant: most of the variables change
throughout the game play and as these are displayed the length of the line
changes. Use `make test' to get an estimate (it's not perfect).
changes. Use `make test` to get an estimate (it's not perfect).
This is not the fault of my entry but rather how text wraps. But this is another
reason it's easy to change: if the terminal size is too short then you can
@@ -215,78 +234,83 @@ For an example this would only include the size of the snake and the number of
bugs eaten (I think I have this correct; I've made many changes to the point of
not wanting to mess with anything that's unnecessary):
#define J Z(SIZE: %zu BUGS: %zu\n), A, c);
```c
#define J Z(SIZE: %zu BUGS: %zu\n), A, c);
```
More generally the following expressions and data types (with format specifiers)
come to mind as information that might be of interest.
DESCRIPTION EXPRESSION TYPE FORMAT NOTES
SPECIFIER
DESCRIPTION EXPRESSION TYPE FORMAT SPECIFIER
Snake size A size_t %zu
Y coordinate Y int %d
of snake head
X coordinate X int %d
of snake head
Max snake size g size_t %zu
Bugs eaten c size_t %zu
Bug line (Y) *U int %d
Bug column (X) *V int %d
Snake size A size_t %zu
Growth size (per N size_t %zu
bug)
Snake size A size_t %zu
Y coordinate Y int %d
of snake head
X coordinate X int %d
of snake head
Max snake size g size_t %zu
Bugs eaten c size_t %zu
Bug line (Y) *U int %d
Bug column (X) *V int %d
(Initial) Snake size A size_t %zu
Growth size (per N size_t %zu
bug)
# Make it so the snake starts at a random location
## Make it so the snake starts at a random location
Quite simply change:
X = H / 2; Y = *x / 2;
```c
X = H / 2; Y = *x / 2;
```
To be something like:
X = R(1,H-1);
Y = R(2,*x-1);
```c
X = R(1,H-1);
Y = R(2,*x-1);
```
Or whatever you might want it to be... (question to answer: will this work at
all? You decide!)
* ADDITIONS (+)
# Additions (+)
Here are some things you could add (including a bit of code).
# Health (and/or Lives)
## Health (and/or Lives)
This was something I thought of a number of times but I opted for other features
instead. Here are some ideas on how one might choose to implement something like
this. Or maybe it's more like what sorts of things do you have to consider and
answer?
(1) If the snake runs back into itself - thus dying - the snake would actually
be slithering inside itself (a rather creepy thought - though they sort of do
when they shed their skin). How then would the lives be of any value? If that
would constitute as running into itself wouldn't it very quickly lose all lives?
0. If the snake runs back into itself - thus dying - the snake would actually be
slithering inside itself (a rather creepy thought - though they sort of do when
they shed their skin). How then would the lives be of any value? If that would
constitute as running into itself wouldn't it very quickly lose all lives?
I can think of a number of ways to answer this question: by defining specific
rules (as in what would be allowed and what wouldn't be) and also by when to
decrement the lives.
I can think of a number of ways to answer this question: by defining
specific rules (as in what would be allowed and what wouldn't be) and also
by when to decrement the lives.
(2) If the snake hits a wall and it's not allowed to where would the snake be
1. If the snake hits a wall and it's not allowed to where would the snake be
placed? This is similar to (1) but maybe not as many things to consider.
(3) Should the snake lose its size? WHAT does having another life even MEAN?
2. Should the snake lose its size? WHAT does having another life even MEAN?
(4) Do you want (you do not necessarily need) a new variable that says how many
3. Do you want (you do not necessarily need) a new variable that says how many
lives you want? Either way probably be good that it can be specified by the
player.
@@ -305,12 +329,12 @@ some other complications.
You could do similar for health only maybe the size of the snake decreases or
you use the variable you would have used for lives to be health instead.
Vital issue to resolve: Where is the snake? And if the snake size is decreased
Vital issue to resolve: where is the snake? And if the snake size is decreased
will you be careful enough that you don't run into the snake where it actually
isn't?
# Saving the game
## Saving the game
A few things would have to be done to do this. Maybe more than a few: some
decisions and some code.
@@ -329,8 +353,8 @@ the number of bugs eaten so far. Alternatively you could have the bug simply be
placed somewhere where the snake isn't after replacing (can you replace
something with itself?) the snake.
Question: Do you have to save the WALLS and other environmental variables?
You wouldn't have to but you could: If you didn't it would mean that the player
Question: do you have to save the WALLS and other environmental variables?
You wouldn't have to but you could: if you didn't it would mean that the player
could change the growth size on another run for example, or they could disable
or enable a feature.
@@ -355,40 +379,43 @@ It might seem like a difficult task but it's actually not that difficult. It
didn't take much time for me to do something like this: as far as saving and
loading the values and even setting up ncurses: I just didn't go beyond that.
Tip: Don't try scr_dump()/scr_restore()/scr_init()/scr_set(). After a quick
Tip: **DON'T TRY** `scr_dump()`/`scr_restore()`/`scr_init()`/`scr_set()`. After a quick
test in the way I have things set up it seems to cause some problems (also
scr_dump() quits the program).
`scr_dump()` quits the program).
You would have to instead identify that restoring is requested, then read in the
file (including the coordinates) and then populate the snake arrays, drawing it
on screen.
# Different types of walls
## Different types of walls
The game has two modes with walls: if you run into it the game is over (you
die) OR you go through it and come out on the opposite side. There certainly are
other possibilities though. Here I give some ideas that could be implemented
(and one I have some code for).
# Different types of walls: Blockades
### Different types of walls: Blockades
Instead of the snake either dying or going through and out on the other side
what if it's just that the snake is momentarily dazed and unable to move again
until it comes round sort of like what happens with some people who pay more
attention to their ruddy phone instead of where they're walking (even when
crossing a busy street!).
I did this once before as an experiment so it can work fine: let's say instead
of the snake either dying or going through and out on the other side what if
it's just that the snake is momentarily dazed and unable to move again until it
comes round sort of like what happens with some people who pay more attention to
their ruddy phone instead of where they're walking (even when crossing a busy
street!).
If you look at the walls code you will see a ternary operator for both X and Y
If you look at the walls code you will see a ternary operator for both `X` and `Y`
coordinates. This of course is very different now but the idea remains the same:
if (p && !(*O>=O[1]||H>=M(1)))
{
if (X<1||X>=H)
X = X<1?H-1:1;
if (Y<2||Y>=*O)
Y = Y<2?*O-1:2;
}
```c
if (p && !(*O>=O[1]||H>=M(1)))
{
if (X<1||X>=H)
X = X<1?H-1:1;
if (Y<2||Y>=*O)
Y = Y<2?*O-1:2;
}
```
You can ignore the first if if you like; I'm not going to explain that part. The
@@ -399,9 +426,11 @@ coordinates to the correct new location.
If however you wanted the walls to act as a barrier you could do something like
this instead (omitting the inner if statements here):
X=X<1?1:*H-1;
```c
X=X<1?1:*H-1;
Y=Y<2?2:*O-1;
Y=Y<2?2:*O-1;
```
Again though that's just the idea; the code is dramatically different. The above
incidentally is not even complete.
@@ -421,22 +450,25 @@ other words the snake will just appear to be not moving as if the game had been
paused.
# Diagonal movement
## Diagonal movement
I said this is impractical so how could it be done? Well you have to decide
which keys would take you up and right, up and left, down and right or down and
left. The trouble is ncurses can't detect both right and up at the same time.
Besides that though it would only be a matter of updating both Y and X
Besides that though it would only be a matter of updating both `Y` and `X`
coordinates rather than just one or the other. That's easy enough to do. The
only impractical part of this is deciding the keys and unfortunately rule 2b
(and probably 2b) would have been broken and so have no chance of winning (now
that I won it's too much effort and I've forgotten some of the stuff too to say
that I won it's too much effort and I've forgotten most of the stuff too to say
nothing of getting the format right).
Anyway it's just a matter of updating both X and Y. But what about going through
walls mode? Does it matter? I'll let you figure it out!
Anyway it's just a matter of updating both `X` and `Y`. But what about going
through walls mode? Does it matter? I'll let you figure it out!
# Special abilities
Looking back at the missing features, this is the feature I wanted more than any
other.
## Special abilities
Could the snake jump over itself if it wanted to? How would this be implemented?
The head would simply be moved one beyond but then you would have to keep track
@@ -446,7 +478,7 @@ actually includes it! Then it would only be a matter of deciding what key does
what.
# Obstacles
## Obstacles
Similar to walls only that it's inside the field. You would have to consider
the behaviour of running into the obstacles: should the snake die? Should it
@@ -457,7 +489,7 @@ character. Also to make it simpler if the walls are passable then so too would
be the obstacles; else you would die (or if you had health/lives then those
would apply).
# More than one bug
## More than one bug
The main thing to consider would be the test if the snake has eaten a bug would
have to check an array (and: how big? Where does the size come from?) as well as
@@ -466,14 +498,14 @@ meaning that if you run into a bug spot that has more than one bug you would eat
that many bugs).
# Power-ups
## Power-ups
You would need to decide what power-ups exist, what they do, how many can there
be (and how many can the snake have at one time), what makes them show up plus
you would have to add code to handle the power-ups.
# Snakeskin shedding (becoming an obstacle)
## Snakeskin shedding (becoming an obstacle)
I.e. at some point the snake would split back to some (arbitrary) size but then
the snakeskin left would act as the snake in the sense that if the snake runs
@@ -483,18 +515,18 @@ slithered out. But that would fill the screen very quickly of course. Maybe the
snakeskin has some other purpose?
# Snake moves faster over time
## Snake moves faster over time
In the game Surround you have no control over the speed. I believe there are
five different speed levels but the player has no control over it except the
drawing mode. It would be possible to make it so the snake moves faster over
time. How often and by what amount would probably vary but importantly it would
need to not go so fast there's no controlling - or go below 0 which puts it in
blocking mode. Probably it would be that it gets faster up to a certain per cent
of the original speed so that it doesn't get too fast. But even that would have
to be customisable.
In the game [Surround](https://en.wikipedia.org/wiki/Surround_(video_game) you
have no control over the speed. I believe there are five different speed levels
but the player has no control over it except the drawing mode. It would be
possible to make it so the snake moves faster over time. How often and by what
amount would probably vary but importantly it would need to not go so fast
there's no controlling - or go below 0 which puts it in blocking mode. Probably
it would be that it gets faster up to a certain per cent of the original speed
so that it doesn't get too fast. But even that would have to be customisable.
# Max times the bug evades before game over
## Max times the bug evades before game over
Basically so that the player is on a time schedule of sorts. The same could be
done for maximum number of movements before game over. These would both be
+1 -1
View File
@@ -114,7 +114,7 @@ PROG= prog
#
OBJ= ${PROG}.o
DATA= COMPILING.md HACKING.md bugs.html bugs.md cake.jpg cannibalism.log.md \
chocolate-cake.html chocolate-cake.md crazy.log gameplay.html gameplay.md \
chocolate-cake.html chocolate-cake.md crazy.log.md gameplay.html gameplay.md \
halfwidth_vs_fullwidth.png ioccc-cake.jpg play.sh prog.2.c prog.3-j.c \
prog.3.c snake-colours.sh snake.1 spoilers.html spoilers.md \
termcaps.c terminals.html terminals.md troubleshooting.html troubleshooting.md
+65 -84
View File
@@ -15,62 +15,67 @@ make
```sh
WAIT=N WALLS=[01] EVADE=N SIZE=N MAXSIZE=N GROW=N SHEDS=N SHED=N CANNIBAL=[01] ./prog
# Start pressing some arrow keys
# start pressing some arrow keys
```
Variables:
where `N` is a number and `[01]` is either `1` or `0` (or more correctly zero
and non-zero).
* WAIT
### Variables:
* **`WAIT`**
positive or negative integer that changes how long to wait for
a key press prior to moving again. < 0 blocks which allows
for complete control over movement at your own pace. Default
231; you can move faster by holding down an arrow key.
231; you can move faster by holding down or rapidly pressing an
arrow key.
* WALLS
whether the walls are passable or impassable (default
passable). 0 means impassable; 1 passable.
* **`WALLS`**
whether the walls are passable or impassable. Default passable,
1; 0 disables.
* EVADE
* **`EVADE`**
how many movements the Snake moves before the bug (well
insect; see notes below about why bugs) is will move to
another location on the field.
insect; see notes below about why bugs) will move to
another location in the field. Default 300; 0 disables.
* SIZE
* **`SIZE`**
initial size of the Snake. Note that it grows by one per
move so at the default 5 it will not initially be 5
characters long! < 0 is an immediate win.
* MAXSIZE
* **`MAXSIZE`**
the maximum size the Snake can become before the game is
won. < 0 is the maximum size based on terminal dimensions;
any other size will be capped based on the terminal
dimensions to prevent problems.
dimensions to prevent problems. Note that when **`SIZE+GROW >=
MAXSIZE`** you win _before_ you grow to the full size!
* GROW
size to grow every time you eat a bug. Negative values are
allowed! This creates for fun gameplay modes with enough
creativity. See [gameplay.html](gameplay.html) for some
examples.
* **`GROW`**
size to grow every time you eat a bug. Default 5; negative
values are allowed. 'Negativity' creates for fun gameplay modes with
enough creativity. See [gameplay.md](gameplay.md) /
[gameplay.html](gameplay.html) as well as [play.sh](play.sh) for some examples.
* SHED
every SHED movements you will grow (> 0), shrink (< 0) or
not at all (0) by the SHEDS value. Again with creativity you
can create some fun gameplay modes.
NOTE: SHED and SHEDS is actually a misnomer; see notes
* **`SHED`**
every **`SHED`** movements you will grow (> 0), shrink (< 0) or
stay the same length (0) by the **`SHEDS`** (below) value. Again
with creativity you can create some fun gameplay modes. NOTE:
the concept of shedding in this game is a misnomer; see notes
below.
* SHEDS
every SHED movements will impact what this value means: grow
(> 0), shrink (< 0) or not at all (0). Again with creativity
you can create some fun gameplay modes here. For instance
you can grow upon eating a bug but shrink every SHED
movements and it's a battle of whether you win by SIZE < 0
or SIZE >= MAXSIZE. See play.sh for examples.
NOTE: SHED and SHEDS is actually a misnomer; see notes
below.
* **`SHEDS`**
every **`SHED`** movements will impact what this value means: grow
(> 0), shrink (< 0) or not at all (0). Exception: if the snake
eats a bug the counter isn't incremented. Again with creativity
you can create some fun gameplay modes here. For instance you
can grow upon eating a bug but shrink every **`SHED`** movements
and then it's a battle of whether you win by **`SIZE < 0`** or
**`SIZE >= MAXSIZE`**. See [play.sh](play.sh) for examples.
NOTE: the concept of shedding in this game is a misnomer; see
notes below.
* CANNIBAL
* **`CANNIBAL`**
whether you can go through the Snake or not. Default is 0
(cannot).
@@ -137,52 +142,27 @@ Read ***ONLY*** if you really must give up trying to de-obfuscate!
Snake has two cheat modes (passable walls and self [cannibalism]), a drawing (or
practising) mode, *can play by itself (**and win!**)*, is coloured (included
[snake-colours.sh][] and [play.sh][] scripts compile in player-selected
colours) and there are many other play modes (many more can be devised with the
[snake-colours.sh][] and [play.sh][] scripts compile in player-selected colours)
and there are many other play modes (many more can be devised with the
imagination and the environmental variables; see [gameplay.html][] for many
examples) and options. You can pause and there's even a built-in test unit for
some features! The following variables change the game in the following ways
(all can be combined):
examples and [play.sh][] for preconfigured modes) and options. You can pause
and there's even a built-in test unit for some features! The variables
mentioned in the to run section at the top of this file modify the behaviour of
Snake.
Here are some other features of the game:
* The speed of the snake: how many milliseconds to **WAIT** for input before
moving; *default 231 milliseconds*. You can move faster by pressing a
direction key quicker or even holding it down.
* If the bug (to eat) will move (**EVADE**) after user-specified number of snake
moves (*default 300, 0 disables*).
* If the snake can go through **WALLS** (coming out at the opposite wall)
(*default 1, yes*).
* If the snake can go through itself (**CANNIBAL**) (*default 0, no*).
* The initial **SIZE** of the snake (*default 5*). Note that it grows one by
each movement so even if you start at 5 it will take some movements before
you are the full length.
* The size the snake must become in order to win the game (**MAXSIZE**, *default 997*).
* How much to **GROW** each time a bug is eaten (*default 5*, can be negative).
* How many moves (**SHED**, *default 0, disabled*) before 'stretching' (grow)
(**SHEDS**, *default 5*; can be negative). This is how snakes grow in the wild so is a
misnomer here because snakes don't grow in the manner that the original
Snake game was designed.
- Exception: if the bug is eaten the counter isn't incremented.
* A drawing/practising mode (**WAIT** < 0 makes `timeout()` set blocking read).
* A drawing/practising mode (**`WAIT`** < 0 makes `timeout()` block).
* Computer plays the game (**WAIT=0**).
- **EPILEPSY/STIMULATION OVERLOAD WARNING** included in the relevant
section (this goes for a low **EVADE** value too).
section (this goes for a low **`EVADE`** value too).
* Grow-Shrink mode (Positive and Negative Winning) mode (see
[gameplay.html][] for more details).
* The dimensions of the game (this is actually a terminal thing but I document
how to do this and its potential pitfalls) (**LINES** , **COLUMNS**).
how to do this and its potential pitfalls) (**`LINES`** , **`COLUMNS`**).
There are no complicated command line invocations; it's just a matter of passing
into the game descriptively named variables and I include a script that sets up
@@ -228,7 +208,7 @@ though much of it is what's also above). To render try:
man ./snake.1
```
The [COMPILING][] file has a few portability notes and the [HACKING][] file has
The [COMPILING.md][] file has a few portability notes and the [HACKING.md][] file has
some information on how one might modify things, things that could be
implemented (and how to/how not to go about some of the things) as well as some
other information on the entry (some of which is deliberately not true - a
@@ -308,7 +288,8 @@ everything? That's an unintentional lie; I thought it was everything but I
implemented many new things after that. In fact I have numerous times thought I
was done only to have added yet another feature - or two or three or more!
Second is the old Atari game [Surround][]. There were a few modes as I recall.
Second is the old Atari game [Surround][] (according to Wikipedia it might even
be the origin of Snake itself). There were a few modes as I recall.
The object of the game was to surround your opponent so that they would run into
you, the wall or themselves. The opponent was either the computer or another
player.
@@ -318,11 +299,11 @@ likewise). There was also a drawing mode where you could just move round making
different designs though that this is in my entry is a happy coincidence
(because `timeout()` will block when it gets a negative value). It's true that
in my Snake it's not quite the same but it's close enough. Perhaps it can be
made more like Surround if it grows every movement but then depending on the max
made more like [Surround][] if it grows every movement but then depending on the max
size of the snake the game might be over fairly quickly (I don't remember how
it was done in Surround).
it was done in [Surround][]).
In Surround each move you made you would grow in size by one. This is what
In [Surround][] each move you made you would grow in size by one. This is what
inspired the shedding mode. Anyway I always loved the game and it's similar to
Snake in several ways.
@@ -427,7 +408,7 @@ And if you're a show-off you can try:
- Do more than one or all of the above at the same time (the longer the snake
becomes the more often you should do these things).
- Eat a [great chocolate cake][]. Actually you should do that whether you're
trying to show off or not. Sleep, bathe, eat chocolate cake, play Snake, eat
trying to show off or not. Sleep, bath, eat chocolate cake, play Snake, eat
chocolate cake, play Snake, sleep, ... For a great recipe see below.
But if that's not enough I don't know what to tell you other than suggest that
@@ -441,7 +422,7 @@ a pun and every pun deserves to be shed).
They grow through a process called moulting - shedding their skin (other
creatures also moult). This takes place over some days and unlike humans snakes
never stop growing (though growth slows down as they mature). I chose the
variables **SHED** and **SHEDS** for simplicity and because the words are
variables **`SHED`** and **`SHEDS`** for simplicity and because the words are
shorter. I don't like the words because it's rather a misnomer but what I had
chosen before was longer - STRETCH and STRETCHES.
@@ -461,10 +442,10 @@ when other implementations don't have it this way. There are a few reasons.
First is that the friend's implementation had it that way and it had been a long
time since I had played it (if I ever did? I don't know now: I played so many
games over the years and the game most alike it that I played is Surround, as
games over the years and the game most alike it that I played is [Surround][], as
below).
It also is the way it is in Surround.
It also is the way it is in [Surround][].
It makes cannibalism mode very interesting.
@@ -512,13 +493,13 @@ out bad!
I submitted three Snake versions; these are the other layouts for those few
(more likely none) who are interested to see them.
The prog.2.c has more digraphs but I think no other significant differences.
The [prog.2.c](prog.2.c) has more digraphs but I think no other significant differences.
The prog.3.c is in a more artistic layout (a backwards S), has at least one
The [prog.3.c](prog.3.c) is in a more artistic layout (a backwards S), has at least one
additional obfuscation technique (see spoilers) but it also has more digraphs
and as the judges said they feel that digraphs are dated so that's probably why
they chose the first layout. The prog.3-j.c version is prog.3.c with a shorter J
define.
they chose the first layout. The [prog.3-j.c](prog.3-j.c) version is
[prog.3.c](prog.3.c) with a shorter `J` macro.
The prog.alt.c version is the one that allows for customising the bug colour and
it's used in both [snake-colours.sh][] and [play.sh][]. It also calls `erase()`
@@ -577,8 +558,8 @@ my entries. It's a huge honour; thank you! I also happen to **love** your
comments as well as the award titles. And yes indeed 'most of us could use
*[Double-layered Chocolate Fudge Cake][]*!'
[COMPILING]: COMPILING
[HACKING]: HACKING
[COMPILING.md]: COMPILING.md
[HACKING.md]: HACKING.md
[bugs.html]: bugs.html
[troubleshooting.html]: troubleshooting.html
[human snakes]: http://www.macroevolution.net/snake-human-hybrids.html
+62 -38
View File
@@ -30,7 +30,9 @@ simply a fun consequence of the features I did implement.
Let's say you want to verify that the evade mode is at the right count. How
can you verify it without modifying the code? Try:
SIZE=0 SHED=1 SHEDS=1 EVADE=10 ./prog
```sh
SIZE=0 SHED=1 SHEDS=1 EVADE=10 ./prog
```
Since you start out at size 0 and you shed every movement then when you're
size 10 (as long as you don't eat a bug of course!) the bug should evade.
@@ -50,7 +52,9 @@ don't at this time remember any I might have done.
One way to show this is in full is to set the initial size and growth size to a
high value; then try each one individually. For example:
SIZE=100 ./prog
```sh
SIZE=100 ./prog
```
In this case after you make the first move the snake will keep growing and
so the snake tail stays in the same spot until after it's the 'full size'.
@@ -59,7 +63,7 @@ start to move too (i.e. if the tail isn't moving it is growing or else paused).
In several tests I set the initial size to a high value, the max to a higher
value still and enabled cannibalism and much of the screen was full of the snake
before it started to move. The [cannibalism.log][] has some examples of both
before it started to move. The [cannibalism.log.md][] has some examples of both
cannibalism and a long snake that didn't move for most of the game.
If however you let the snake get to the full size then eat a bug there can
@@ -91,7 +95,7 @@ you can safely go back on yourself even without cannibalism enabled. This is
intentional; a snake size of 0 is pointless but why should it not be playable
anyway?
If SIZE < 0 you'll likely win straight away since it's unsigned and it'll wrap
If `SIZE < 0` you'll likely win straight away since it's unsigned and it'll wrap
back to the maximum so you will win automatically (this is the same as the
second mode the judges suggested with `SIZE=-1 ./prog`).
@@ -111,20 +115,28 @@ way negative size will force a win.
Here's a fun output:
< <<<+
```
< <<<+
```
That's from:
SHED=1 SHEDS=-1 ./prog
```sh
SHED=1 SHEDS=-1 ./prog
```
And right after that you would see:
YOU WIN!
X:72/156 Y:20/41 S:997/997 B:0
```
YOU WIN!
X:72/156 Y:20/41 S:997/997 B:0
```
Try this though:
SHEDS=-1 SHED=25 ./prog
```sh
SHEDS=-1 SHED=25 ./prog
```
And you'll see that you start to be the normal size but then you leave your
head several places until you end up getting to the max size (by way of it
@@ -134,14 +146,18 @@ this is the basis of the grow/shrink mode of play that I talk about in the
Here's another thing to be aware of. Let's say you do:
SIZE=1 GROW=-1 ./prog
```sh
SIZE=1 GROW=-1 ./prog
```
How many bugs will it take before you win? In fact it's two: you would see the
head twice: the second time once you 'shrink' down to size 0; it would then be
that if you get another bug you will be at the 'max size':
YOU WIN!
X:81/156 Y:35/41 S:997/997 B:2
```sh
YOU WIN!
X:81/156 Y:35/41 S:997/997 B:2
```
# <a name="bitetail" href="#toc">'I was a few places behind my tail and I ran into it!'</a>
@@ -161,17 +177,21 @@ For instance it might look like this (the first line is as it happens, the
second pointing to where it happens and the third being what's left after the
rest of the snake has gone through). Doing `SHED=100 SHEDS=-1`:
ooooooooo>ooo>
^
>
```
ooooooooo>ooo>
^
>
```
The ^ points to the part that's split from the snake and which will be
The `^` points to the part that's split from the snake and which will be
there on its own after the snake passes through it. Here's an example with
it being `SHEDS=-3`:
<ooo<ooooo
^^^
<oo
```
<ooo<ooooo
^^^
<oo
```
If you go through the part that has split (which for some might be difficult
to see whilst the snake has not fully left its skin) you won't run into
@@ -187,14 +207,16 @@ gameplay modes.
# <a name="buginsnake" href="#toc">It looks like the bug is in the snake</a>
I show a different example in the [crazy.log][] file but what defines a coordinate
I show a different example in the [crazy.log.md][] file but what defines a coordinate
which the snake occupies? If the snake breaks apart then the only part
of the screen the snake is occupying is the place which is actually moving: if
the snake decreases in size by 3 then the three respective characters you can go
through safely. But this also means that a bug can go there! So you might see
something like:
<*oooooooo
```
<*oooooooo
```
This is because the snake is no longer there: in fact in this run the snake was
quite a bit lower on the screen and 25 in length.
@@ -214,30 +236,32 @@ the game is 10 x 10. This includes the walls and score line.
Now the default max snake size is 997 but when the terminal is too small it
adjusts the max snake size. For 10 lines and 10 columns you would see:
$ make test
terminal supports cursor movement
terminal supports making cursor invisible
terminal supports bold
terminal supports colours
```sh
$ make test
terminal supports cursor movement
terminal supports making cursor invisible
terminal supports bold
terminal supports colours
terminal rows 10 (7 playable)
terminal cols 10 (8 playable)
terminal rows 10 (7 playable)
terminal cols 10 (8 playable)
snake size: 49 (max size: 49)
bugs: 9 (max size: 9)
snake size: 49 (max size: 49)
bugs: 9 (max size: 9)
at least 28 columns recommended for snake size 49 (is 10)
at least 28 columns recommended for snake size 49 (is 10)
at least 28 columns recommended for snake size 49 (is 10)
at least 28 columns recommended for snake size 49 (is 10)
1 problem detected (0 fatal).
make: *** [test] Error 1
1 problem detected (0 fatal).
make: *** [test] Error 1
```
First: what's the problem? It's just a warning that for the snake size of 49 the
recommended size of columns is 28. However this is just for the score text; with
only 10 columns it will overflow and wrap to the next line(s). But if you observe
that 49 is the max snake size. How is this derived and why?
With 10 rows and columns it's calculated as 7 * 7 (i.e. it's
With 10 rows and columns it's calculated as `7 * 7` (i.e. it's
`(cols-3)*(rows-3)`. However the number of playable locations is in this case 56
(`playable_rows * playable_columns`). I have played with this many
times and I do not believe there should be a problem. If there is it's from a
@@ -277,7 +301,7 @@ had to say on it:
<img src="halfwidth_vs_fullwidth.png">
However the function `mvaddch()` takes a `chtype` which amounts to a C char
with additional information and so the full width Ois too big for the
with additional information and so the full width `O`is too big for the
function call. He reminded me of the old DOS box chars but even if this was
easily duplicable (and it might be and I don't remember) the logic would
have to be changed for drawing the snake and in such a way that would change
@@ -294,12 +318,12 @@ suggested it is bugging me I once again here say it's not at all bugging me
but it is very much appreciated and it means a great deal to me. Cheers.)
[crazy.log]: crazy.log
[crazy.log.md]: crazy.log.md
[gameplay.md]: gameplay.md
[troubleshooting.md]: troubleshooting.md
[troubleshooting.html]: troubleshooting.html
[gameplay.html]: gameplay.html
[cannibalism.log]: cannibalism.log
[cannibalism.log.md]: cannibalism.log.md
[https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms]: https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms
[spoilers]: spoilers.markdown
[screenshot of halfwidth vs fullwidth]: halfwidth_vs_fullwidth.png
@@ -1,6 +1,8 @@
The following are a few screen dumps of:
SIZE=100 SHEDS=5 GROW=-10 SHED=50 MAXSIZE=250 ./prog
```sh
SIZE=100 SHEDS=5 GROW=-10 SHED=50 MAXSIZE=250 ./prog
```
As you will see it took 17 bugs to get down to below 0. Notice how I had to get
down to 0 more than once: because I moved enough times before I got the last
@@ -8,12 +10,15 @@ bug that I grew by 5 in size. I then however moved a bit more and ate a bug
which brought me down to -5 therefore winning at the max size of 250. This then
showed:
YOU WIN!
X:62/89 Y:5/25 S:250/250 B:17
```
YOU WIN!
X:62/89 Y:5/25 S:250/250 B:17
```
Observe all the arrows on the screen; that's where a bug was that I ate and
then I split off losing 10 in size. The first one dump I got myself already to 15:
then I split off losing 10 in size. The first dump I'm already at 15/250:
```
X:17/89 Y:15/25 S:15/250 B:13
##########################################################################################
# <ooooooooo o <ooooooooo* #
@@ -40,9 +45,11 @@ X:17/89 Y:15/25 S:15/250 B:13
# o o> #
# oooooo #
##########################################################################################
```
But here now I'm at 0; the head is the sole left arrows:
But here now I'm at 0; the head is marked `H` and it's moving left.
```
X:19/89 Y:12/25 S:0/250 B:16
##########################################################################################
# <ooooooooo o <oooooooo <ooooooo #
@@ -55,7 +62,7 @@ X:19/89 Y:12/25 S:0/250 B:16
# <oo o o o #
# o #
# o o #
# < o o #
# H o o # <- *HEAD, LEFT*
# o o o #
# <ooo o oo ooooo> #
# o o #
@@ -69,16 +76,21 @@ X:19/89 Y:12/25 S:0/250 B:16
# o o> #
# oooooo #
##########################################################################################
```
I ended up moving enough times where my size went back up to 5; the head of the
snake is in this file line 80 col 56; the bug is at line 81 col 63:
I ended up moving enough times where my size went back up to 5.
Below the snake is moving right and its head is indicated by a `H` with the line
marked at the right; the bug is marked as `B` and is also marked at the right.
Notice how it's in the body of the snake (but see below)!
```
X:55/89 Y:4/25 S:5/250 B:16
##########################################################################################
# <ooooooooo o <oooooooo <ooooooo #
# o o> oo #
# oooo> oooo #
# o o oooooo*oo> v #
# ooooH oooo # <- *HEAD, RIGHT*
# o o ooooooBoo> v # <- *BUG*
# o o oo o oo> #
# o o o o oo #
# o o o oooo #
@@ -99,6 +111,7 @@ X:55/89 Y:4/25 S:5/250 B:16
# o o> #
# oooooo #
##########################################################################################
```
But did you see anything strange? The bug is on the fourth line below the upper wall.
However it appears to be inside the body of the snake! But actually that's just where
+308 -237
View File
@@ -78,11 +78,11 @@ For troubleshooting other problems see [troubleshooting.md][]
# 2. <a name="input" href="#toc">Input keys</a>
- Left, right, up and down **arrow keys**: Move that direction.
- Left, right, up and down **arrow keys**: move that direction.
- 'q' (and 'Q'): Quit the game.
- `q` (and `Q`): quit the game.
- Space (`' '`): Pause/resume the game (direction keys will also resume).
- Space: pause/resume the game (direction keys will also resume).
Note that no other keys will resume the game; however if you press a key
that `getch()` picks up on and the game is not paused then it will be as if
you pressed the current direction key (assuming you already moved the snake).
@@ -106,24 +106,26 @@ below. Note that the top line is the score/status line: the top wall is at line
(ncurses is the opposite: Y/X). I will parenthesise the numbers below to refer
to the number in the diagram:
X:24/49 Y:8/16 S:5/658 B:0
##################################################
# #
# #
# #
# #
# #
# #
# + #
# #
# * #
# #
# #
# #
# #
# #
##################################################
```
X:24/49 Y:8/16 S:5/658 B:0
##################################################
# #
# #
# #
# #
# #
# #
# + #
# #
# * #
# #
# #
# #
# #
# #
##################################################
```
- The snake (`+`) is at (24),(8). The bug char is `*` and the wall char is
`#`. When the snake moves the head will be an arrow facing the direction it
@@ -149,66 +151,72 @@ to the number in the diagram:
## <a name="playing" href="#toc">Gameplay: Playing the game</a>
X:39/49 Y:8/16 S:5/658 B:0
##################################################
# #
# #
# #
# #
# #
# #
# oooo> #
# #
# * #
# #
# #
# #
# #
# #
##################################################
```
X:39/49 Y:8/16 S:5/658 B:0
##################################################
# #
# #
# #
# #
# #
# #
# oooo> #
# #
# * #
# #
# #
# #
# #
# #
##################################################
```
I let the snake grow to its full size (with the head at (39),(8)) and I'm two
places above the bug so let's go down:
X:39/49 Y:9/16 S:5/658 B:0
##################################################
# #
# #
# #
# #
# #
# #
# oooo #
# v #
# * #
# #
# #
# #
# #
# #
##################################################
```
X:39/49 Y:9/16 S:5/658 B:0
##################################################
# #
# #
# #
# #
# #
# #
# oooo #
# v #
# * #
# #
# #
# #
# #
# #
##################################################
```
Now you see that the snake is going down and in one move the bug will be eaten.
Let's do that:
X:36/49 Y:10/16 S:10/658 B:1
##################################################
# #
# #
# #
# * #
# #
# #
# #
# #
# ^ o #
# oooo #
# #
# #
# #
# #
##################################################
```
X:36/49 Y:10/16 S:10/658 B:1
##################################################
# #
# #
# #
# * #
# #
# #
# #
# #
# ^ o #
# oooo #
# #
# #
# #
# #
##################################################
```
Observe that the bug was eaten and a new bug spawned elsewhere. I went left
three and then up one and could slither on to the bug.
@@ -220,44 +228,48 @@ By default walls are passable (**WALLS=1**). Let's get to full size and go
through the top wall:
X:36/49 Y:15/16 S:10/658 B:1
##################################################
# o #
# o #
# o #
# * o #
# o #
# o #
# o #
# o #
# o #
# #
# #
# #
# #
# ^ #
##################################################
```
X:36/49 Y:15/16 S:10/658 B:1
##################################################
# o #
# o #
# o #
# * o #
# o #
# o #
# o #
# o #
# o #
# #
# #
# #
# #
# ^ #
##################################################
```
There you can see I have grown to the full size and went through the top wall;
the head is now at the opposite wall. Continuing:
X:36/49 Y:11/16 S:10/658 B:1
##################################################
# o #
# o #
# o #
# * o #
# o #
# #
# #
# #
# #
# ^ #
# o #
# o #
# o #
# o #
##################################################
```
X:36/49 Y:11/16 S:10/658 B:1
##################################################
# o #
# o #
# o #
# * o #
# o #
# #
# #
# #
# #
# ^ #
# o #
# o #
# o #
# o #
##################################################
```
I might then have turned to the left and then shortly go up to get the bug. This
goes on until you lose, quit or win the game.
@@ -266,30 +278,32 @@ goes on until you lose, quit or win the game.
When you specify **WALLS=0** the walls become an obstacle that end the game. It
might look like (I set the starting size to 150 and had only just started):
X:20/49 Y:2/16 S:150/658 B:0
##################################################
# ^ #
# o #
# o * #
# o #
# o #
# o #
# ooooo #
# #
# #
# #
# #
# #
# #
# #
##################################################
```
X:20/49 Y:2/16 S:150/658 B:0
##################################################
# ^ #
# o #
# o * #
# o #
# o #
# o #
# ooooo #
# #
# #
# #
# #
# #
# #
# #
##################################################
```
If I continue going up I will see:
OUCH!
X:20/49 Y:1/16 S:150/658 B:0
```
OUCH!
X:20/49 Y:1/16 S:150/658 B:0
```
@@ -298,95 +312,104 @@ If I continue going up I will see:
By default cannibalism is disabled (**CANNIBAL=0**) so if you run into yourself it might look
something like:
X:45/49 Y:10/16 S:10/658 B:1
##################################################
# #
# #
# #
# * #
# #
# #
# #
# ooooo #
# ^ o #
# ooo #
# #
# #
# #
# #
##################################################
```
X:45/49 Y:10/16 S:10/658 B:1
##################################################
# #
# #
# #
# * #
# #
# #
# #
# ooooo #
# ^ o #
# ooo #
# #
# #
# #
# #
##################################################
```
If I were to do any movement but turn to the left I would run into myself. I'll
go to the right two spaces:
OW!
X:47/49 Y:10/16 S:10/658 B:1
```
OW!
X:47/49 Y:10/16 S:10/658 B:1
```
But **CANNIBAL=1** can look very funny both in movement and still especially
when you go through yourself several times when the snake is rather long. For
example going through walls and the snake a few times:
X:4/49 Y:12/16 S:165/658 B:3
##################################################
#oo o ooooo o o o#
# oooo o ooo o#
# ooooooooooooooooooo#
# o #
# oooooooooo o #
# o o o #
# o oooo o o #
# o o o o #
# ooooooooooooooo #
# o o* #
# oo> o o #
# o oooooooo o #
#oooooooooooooo o oooo oooooooo#
# o o o o o #
##################################################
```
X:4/49 Y:12/16 S:165/658 B:3
##################################################
#oo o ooooo o o o#
# oooo o ooo o#
# ooooooooooooooooooo#
# o #
# oooooooooo o #
# o o o #
# o oooo o o #
# o o o o #
# ooooooooooooooo #
# o o* #
# oo> o o #
# o oooooooo o #
#oooooooooooooo o oooo oooooooo#
# o o o o o #
##################################################
```
I'll go back through myself now:
X:6/49 Y:14/16 S:165/658 B:3
##################################################
#oo o ooooo o o o#
# oooo o ooo o#
# ooooooooooooooooooo#
# o #
# oooooooooo o #
# o o o #
# o oooo o o #
# o o o o #
# ooooooooooooooo #
# o o* #
# ooo o o #
# oo oooooooo o #
#ooooo>oooooooo o oooo oooooooo#
# o o o o o #
##################################################
```
X:6/49 Y:14/16 S:165/658 B:3
##################################################
#oo o ooooo o o o#
# oooo o ooo o#
# ooooooooooooooooooo#
# o #
# oooooooooo o #
# o o o #
# o oooo o o #
# o o o o #
# ooooooooooooooo #
# o o* #
# ooo o o #
# oo oooooooo o #
#ooooo>oooooooo o oooo oooooooo#
# o o o o o #
##################################################
```
If you note that the head is at (6),(14) I will go one more to the right and you
will see that the arrow has moved over:
X:7/49 Y:14/16 S:165/658 B:3
##################################################
#oo o ooooo o o o#
# oooo o ooo o#
# ooooooooooooooooooo#
# o #
# oooooooooo o #
# o o o #
# o oooo o o #
# o o o o #
# ooooooooooooooo #
# o o* #
# ooo o o #
# oo oooooooo o #
#oooooo>ooooooo o oooo oooooooo#
# o o o o o #
##################################################
```
X:7/49 Y:14/16 S:165/658 B:3
##################################################
#oo o ooooo o o o#
# oooo o ooo o#
# ooooooooooooooooooo#
# o #
# oooooooooo o #
# o o o #
# o oooo o o #
# o o o o #
# ooooooooooooooo #
# o o* #
# ooo o o #
# oo oooooooo o #
#oooooo>ooooooo o oooo oooooooo#
# o o o o o #
##################################################
```
Try this: Get a reasonable length and then go back on yourself (if you're going
Try this: get a reasonable length and then go back on yourself (if you're going
left go right etc.) and then after some length (but before you get beyond the
snake) turn (so that you go perpendicular) and then watch as the snake starts
unwinding at one place and then suddenly unwinds another direction.
@@ -398,22 +421,24 @@ The final example has the snake inside itself.
# <a name="shedding" href="#toc">Gameplay: Shedding (stretching)</a>
This feature will make you grow by the value of **SHEDS** for every **SHED**
This feature will make you grow by the value of **`SHEDS`** for every **`SHED`**
movements (the counter isn't incremented when you eat a bug). Basically growing
without eating a bug (stretching would be better name here).
# <a name="winning" href="#toc">Gameplay: Winning</a>
If you ever get to the **MAXSIZE** the game is won. You can force a win by
If you ever get to the **`MAXSIZE`** the game is won. You can force a win by
setting the max size to a smaller value than the size you specify (or the
default) or setting the size to higher than the default (or < 0 which will end
up being > the max size though I suppose there might be some value that makes it
less than again - I'm not going to try and figure that out however). Anyway:
SIZE=-1 ./prog
YOU WIN!
X:24/49 Y:8/16 S:658/658 B:0
```sh
SIZE=-1 ./prog
YOU WIN!
X:24/49 Y:8/16 S:658/658 B:0
```
Observe that the size isn't the default 997: because the screen size is too
small for that value (see [terminals.md][]).
@@ -422,7 +447,8 @@ The reason I chose 997 is that it leaves a bit of a challenge for some (maybe
most) people but it's still winnable. Previously even I couldn't have won unless
maybe a much bigger screen, if both cheat modes were disabled. I probably could
win without any cheat mode enabled on my 24" monitor but I probably would need
at least one cheat mode on my 13" laptop (I don't need both enabled).
at least one cheat mode on my 16" laptop (I don't need both enabled). I don't
think I need either for 997.
The beauty though is the game can be winnable by anyone since you can define the
max size to win. Games are meant to be fun: who enjoys games that can never be
@@ -442,7 +468,9 @@ the game is won is 199 - also a prime!
If you wish to quit you can hit 'q' (case-insensitive). For example:
X:24/49 Y:8/16 S:5/658 B:0
```
X:24/49 Y:8/16 S:5/658 B:0
```
@@ -462,7 +490,7 @@ the modes below.
## <a name="warning" href="#toc">Photosensitive epilepsy/Stimulation overload trigger warnings</a>
If you have **photosensitive epilepsy please be careful** especially with low values
for both **WAIT** and **EVADE**. It appears that the [number of hertz (flashes)
for both **`WAIT`** and **`EVADE`**. It appears that the [number of hertz (flashes)
per second triggering seizures][] is wide but depending on the variables it
easily can be in the range.
@@ -479,19 +507,25 @@ If you want to draw the snake wherever you like on the screen you can do this by
modifying the wait time (for input) to -1 (or any value < 0): then the snake
does not move until you press a movement key. For example:
WAIT=-1 ./prog
```sh
WAIT=-1 ./prog
```
When you eat a bug the snake will grow and you can draw more. If you want to
make it so you can go through yourself:
WAIT=-1 CANNIBAL=1 ./prog
```sh
WAIT=-1 CANNIBAL=1 ./prog
```
If you don't want to start out so small (i.e. you want to be able to draw more
quickly) you can just set the size to a bigger number (and you could do the same
with growth and moult/shed):
SIZE=50 CANNIBAL=1 WAIT=-1 ./prog
```sh
SIZE=50 CANNIBAL=1 WAIT=-1 ./prog
```
Do note that although you're a bigger size you still grow one movement at a
time.
@@ -499,12 +533,16 @@ time.
Okay but then if you get too many bugs the drawing will be over. If this proves
to be a problem simply do:
SIZE=100 CANNIBAL=1 WAIT=-1 GROW=0 ./prog
```sh
SIZE=100 CANNIBAL=1 WAIT=-1 GROW=0 ./prog
```
Or something like that (maybe make the size bigger but that's up to you). You
could also set the max size to be the highest possible by saying
MAXSIZE=-1
```sh
MAXSIZE=-1
```
Which I've done in the [play.sh][] script.
@@ -515,11 +553,14 @@ perfect: the snake after all does not leave gaps and the game does not support
diagonal movement and I also made a mistake and had to modify what I was aiming
for. But you can consider this mode drawing or practice mode.
oooooooooo ooooo o oo ooooo
o o oo o oo o ooo o
oooo o oo ooooooo oooo ooooo
o o ooo oo o ooo o
oooo o oo ooo oooooooo>
oooooooooo ooooo o oo ooooo
o o oo o oo o ooo o
oooo o oo ooooooo oooo ooooo
o o ooo oo o ooo o
oooo o oo ooo oooooooo>
## <a name="auto" href="#toc">Gameplay mode: Computer playing / Automatic mode (Photosensitive epilepsy/Stimulation overload trigger)</a>
@@ -535,7 +576,9 @@ too.
If you're still here you can create this effect like:
WAIT=0 WALLS=1 CANNIBAL=1 ./prog
```sh
WAIT=0 WALLS=1 CANNIBAL=1 ./prog
```
As long as the bug evades you'll find that the snake is growing because it's
actually eating bugs; the computer is actually playing the game by itself! You
@@ -559,14 +602,18 @@ so with fewer available it has a harder time finding a place in that one line.
Here's how to make the bug move every time the snake moves:
CANNIBAL=1 WALLS=1 WAIT=0 EVADE=1 ./prog
```sh
CANNIBAL=1 WALLS=1 WAIT=0 EVADE=1 ./prog
```
#### Help the computer win fairly quickly
If you want to see the game 'win' try something like:
CANNIBAL=1 WALLS=1 WAIT=0 EVADE=1 MAXSIZE=10 ./prog
```sh
CANNIBAL=1 WALLS=1 WAIT=0 EVADE=1 MAXSIZE=10 ./prog
```
#### Negative growth (Shrink) Automatic mode
@@ -574,7 +621,9 @@ If you want to see the game 'win' try something like:
Here's a fun one I just thought of (a long time after these other modes were
thought of):
WAIT=0 EVADE=1 GROW=-1 SIZE=9 MAXSIZE=10 CANNIBAL=1 ./prog
```sh
WAIT=0 EVADE=1 GROW=-1 SIZE=9 MAXSIZE=10 CANNIBAL=1 ./prog
```
What you'll see is that first the snake will get to the size of 9 - whether it
shows to be 10 characters depends on whether or not it gets a bug before it can
@@ -587,7 +636,9 @@ then the head again (i.e. 1 then 0 in size) and then win! This is also in the
Of course you can also do something like:
WAIT=5 WALLS=1 CANNIBAL=1 ./prog
```sh
WAIT=5 WALLS=1 CANNIBAL=1 ./prog
```
Which although might not seem like it is significantly slower.
@@ -604,13 +655,17 @@ of success in catching bugs? Obviously the dimensions will affect this value.
How long will it take for the snake to run into itself? For instance by doing:
WAIT=0 ./prog
```sh
WAIT=0 ./prog
```
On my 13" MacBook Pro at the default settings going horizontally the snake ran
into itself at:
On my 13" MacBook Pro (replaced later by a 16") at the default settings going
horizontally the snake ran into itself at:
OW!
X:57/156 Y:20/41 S:155/997 B:30
```
OW!
X:57/156 Y:20/41 S:155/997 B:30
```
What if you set the size to a high value? The lowest? There are bound to be
other games to play with this feature!
@@ -625,7 +680,9 @@ You have a negative shedding growth and a positive growth size: negative and
positive winning. Depending on the parameters specified it might go one way over
the other. Try:
SIZE=150 SHEDS=-5 GROW=10 SHED=50 MAXSIZE=250 ./prog
```sh
SIZE=150 SHEDS=-5 GROW=10 SHED=50 MAXSIZE=250 ./prog
```
What this does is set the size to 150 (to make it not go down so quickly) and
then for each bug you eat it adds 5 to the size; for every 50 moves (that you
@@ -639,13 +696,15 @@ The parameters above were chosen after playing with the mode a bit to find a
value that wouldn't take that much time but still would pose a challenge. One
could reverse it so that one shrinks more than growing.
The [crazy.log][] file has several screen dumps with this idea.
The [crazy.log.md][] file has several screen dumps with this idea.
You could also have it so that eating a bug makes you shrink and moving so many
moves you grow. I tried this and for me it was far more fun. The [play.sh][]
also has this mode:
SIZE=100 SHEDS=5 GROW=-10 SHED=50 MAXSIZE=250 ./prog
```sh
SIZE=100 SHEDS=5 GROW=-10 SHED=50 MAXSIZE=250 ./prog
```
Obviously there are many other variations. The [play.sh][] script has some
of them (the above is option 5; others are 4, 15 and 16 but only options 4 and 5
@@ -665,7 +724,9 @@ snake moving too.
Anyway this might sound absurd but it can be somewhat hypnotic by doing:
EVADE=1 CANNIBAL=1 ./prog
```sh
EVADE=1 CANNIBAL=1 ./prog
```
And just moving the snake about not trying to catch any bugs - just let whatever
happens happen.
@@ -679,7 +740,9 @@ the growth size to 0 and then just move the snake round and round eating bugs
until you want to quit. You can enable cannibalism so that you don't have to
worry about even running into yourself and losing:
GROW=0 CANNIBAL=1 ./prog
```sh
GROW=0 CANNIBAL=1 ./prog
```
## <a name="expansibility" href="#toc">Gameplay mode: Expansibility</a>
@@ -708,11 +771,15 @@ this file discusses as well as allowing to easily change the colours.
To do this use the [snake-colours][] script like so:
./snake-colours
```sh
./snake-colours
```
If you want to pass in specific variables you can do that too e.g.:
CANNIBAL=1 ./snake-colours
```sh
CANNIBAL=1 ./snake-colours
```
The script will prompt you for the colour of the walls, snake head, body and the
bug.
@@ -729,11 +796,15 @@ you choose uses then the script will override what you pass in.
To use you can do something like:
./play.sh
```sh
./play.sh
```
Or to make it so walls are an obstacle:
WALLS=0 ./play.sh
```sh
WALLS=0 ./play.sh
```
# 6. <a name="variables" href="#toc">Notes about some of the game variables</a>
@@ -750,7 +821,7 @@ snake to move slower you have to increase the wait time!
## <a name="evade" href="#toc">EVADE: Bug evasion frequency</a>
For every **EVADE** snake movements the bug will try and find a new place to
For every **`EVADE`** snake movements the bug will try and find a new place to
occupy. It might be very close to it and I do not ensure it's a new place at
all.
@@ -808,7 +879,7 @@ years ago but I know it's true now. **Believe in yourself. Always.**
[number of hertz (flashes) per second triggering seizures]: https://www.epilepsysociety.org.uk/photosensitive-epilepsy#.Xjgpwi2ZOgQ
[termcaps.c]: termcaps.c
[cannibalism.log.md]: cannibalism.log.md
[crazy.log]: crazy.log
[crazy.log.md]: crazy.log.md
[play.sh]: play.sh
-----------------------------------------------------------------------------------------------------
+113 -125
View File
@@ -2,50 +2,27 @@
.SH NAME
snake \- Feature\-rich curses implementation of the classic 'Snake' game with many gameplay modes and options for IOCCC 2020
.SH SYNOPSIS
\fBsnake\fP
\fBWAIT\fP=\fIN\fP \fBWALLS\fP=\fI[01]\fP \fBEVADE\fP=\fIN\fP \fBSIZE\fP=\fIN\fP \fBMAXSIZE\fP=\fIN\fP \fBGROW\fP=\fIN\fP \fBSHEDS\fP=\fIN\fP \fBSHED\fP=\fIN\fP \fBCANNIBAL\fP=\fI[01]\fP ./snake
.br
# start pressing some arrow keys
.br
where
.I N
is a number and
.I [01]
is either
.I 1
or
.I 0
(or more correctly zero and non-zero).
.SH DESCRIPTION
\fBsnake\fP has two cheat modes (passable walls/self[cannibalism]), a drawing/practising mode, \fIcan play by itself\fP (\fBand win!\fP), is coloured (included \fIsnake-colours.sh\fP and \fIplay.sh\fP scripts compile in player\-selected colours) and there are many other play modes (many more can be devised with the imagination and the environmental variables; see \fbENVIRONMENT\fP and the file \fIgameplay.md\fP for many examples) and options.
\fBsnake\fP has two cheat modes (passable walls/self[cannibalism]), a drawing/practising mode, \fIcan play by itself\fP (\fBand win!\fP), is coloured (included \fIsnake-colours.sh\fP and \fIplay.sh\fP scripts compile in player\-selected colours) and there are many other play modes (many more can be devised with the imagination and the environmental variables; see \fBENVIRONMENT\fP and the file \fIgameplay.md\fP for many examples) and options.
You can pause and there's even a built\-in test unit for some features!
Some of the configurable features are:
.PP
Besides the environmental variables (described below) that create for a lot of gameplay modes, here are some additional features:
.PP
.RS
\fB\-\fP The speed of the snake (how many milliseconds to \fBWAIT\fP for input before moving; \fIdefault 231 milliseconds\fP and you can move faster by pressing the direction key quicker or or even holding it down).
.RE
.PP
.RS
\fB\-\fP If the bug (to eat) will move (\fBEVADE\fP) after user\-specified number of snake moves (\fIdefault 300, 0 disables\fP).
.RE
.PP
.RS
\fB\-\fP If the snake can go through \fBWALLS\fP (coming out at the opposite wall) (\fIdefault 1, yes\fP).
.RE
.PP
.RS
\fB\-\fP If the snake can go through itself (\fBCANNIBAL\fP) (\fIdefault 0, no\fP).
.RE
.PP
.RS
\fB\-\fP The initial \fBSIZE\fP of the snake (\fIdefault 5\fP).
.RE
.PP
.RS
\fB\-\fP The size the snake must become in order to win the game (\fBMAXSIZE\fP, \fIdefault 997\fP).
.RE
.PP
.RS
\fB\-\fP How much to \fBGROW\fP each time a bug is eaten (\fIdefault 5\fP, can be negative).
.RE
.PP
.RS
\fB\-\fP How many moves (\fBSHED\fP, \fIdefault 0, disabled\fP) before 'stretching' (grow) (\fBSHEDS\fP, \fIdefault 5\fP; can be negative).
This is how snakes grow in the wild so is a misnomer here because snakes don't grow in the manner that the original Snake game was designed.
.RS
\fB\- Exception\fP: If the bug is eaten the counter isn't incremented.
.RE
.RE
.PP
.RS
\fB\-\fP A drawing mode (\fBWAIT < 0\fP makes \fBtimeout()\fP set blocking read).
\fB\-\fP A drawing mode (\fBWAIT < 0\fP makes \fBtimeout()\fP block).
.RE
.PP
.RS
@@ -58,10 +35,6 @@ This is how snakes grow in the wild so is a misnomer here because snakes don't g
.RE
.PP
.RS
\fB\-\fP The dimensions of the game (this is actually a terminal thing but I document how to do this and its potential pitfalls) (\fBLINES\fP, \fBCOLUMNS\fP).
.RE
.PP
.RS
\fB\-\fP Different colours and characters for the bug, snake head, snake and walls:
.RS
\fB\-\fP Bug is \fB*\fP (white).
@@ -81,69 +54,89 @@ This is how snakes grow in the wild so is a misnomer here because snakes don't g
The options are specified via the environmental variables.
.SH ENVIRONMENT
The following environmental variables modify the behaviour of the game:
.TP
.B WAIT
positive or negative integer that changes how long to wait for a key press prior to moving again.
< 0 blocks which allows for complete control over movement at your own pace.
Default 231; you can move faster by holding down or rapidly pressing an arrow key.
.TQ
.B WALLS
whether the walls are passable or impassable.
Default 1, passable; 0 means impassable.
.TQ
.B EVADE
how many movements the Snake makes before the bug (well insect) will move to another location in the field.
Default 300; 0 disables.
.TQ
.B SIZE
initial size of the Snake.
Default 5.
Note that it grows by one per move so at the default 5 it will not initially be 5 characters long!
< 0 is an immediate win.
.TQ
.B MAXSIZE
the maximum size the Snake can become before the game is won.
< 0 is the maximum size based on terminal dimensions; any other size will be capped based on the terminal dimensions to prevent problems.
Note that when
.B SIZE + GROW >= MAXSIZE
you win
.I before
you grow to the full size!
.TQ
.B GROW
size to grow every time you eat a bug.
Default 5; can be negative.
"Negativity" creates for fun gameplay modes with enough creativity.
See \fIgameplay.md\fP/\fIgameplay.html\fP for some examples.
\fIplay.sh\fP allows you to reconfigure the colours and play different pre\-configured gameplay modes.
.TQ
.B SHED
every
.B SHED
movements you will grow (> 0), shrink (< 0) or stay the same length (0) by the
.B SHEDS
(below) value.
Default 0.
Again with creativity you can create some fun gameplay modes.
NOTE: the concept of shedding in this game is a misnomer.
\fB\- Exception\fP: if the bug is eaten the counter isn't incremented.
This is how snakes grow in the wild so is a misnomer here because snakes don't grow in the manner that the original Snake game was designed.
.TQ
.B SHEDS
every
.B SHED
movements will impact what this value means: grow (> 0), shrink (< 0) or not at all (0).
Default 0.
Again with creativity you can create some fun gameplay modes here.
For instance you can grow upon eating a bug but shrink every
.B SHED
movements and then it's a battle of whether you win by
.B SIZE
< 0
or
.B SIZE
>=
.B MAXSIZE\c
\&.
See
.I play.sh
and
.I gameplay.html
for examples.
\fB\- Exception\fP: if the bug is eaten the counter isn't incremented.
NOTE: the concept of shedding in this game is a misnomer.
\fB\-\fP How many moves (\fBSHED\fP, \fIdefault 0, disabled\fP) before 'stretching' (grow) (\fBSHEDS\fP, \fIdefault 5\fP; can be negative).
This is how snakes grow in the wild so is a misnomer here because snakes don't grow in the manner that the original Snake game was designed.
.PP
\fBGROW\fP
.RS
How much to grow each time a bug is eaten.
Negative growth is a different gameplay mode.
\fIDefault 5\fP.
.RE
\fBSIZE\fP
.RS
The initial size of the snake.
\fIDefault 5\fP.
.RE
\fBMAXSIZE\fP
.RS
The max snake size (the size the snake must become to win).
\fIDefault 997\fP.
Limits are always imposed depending on the size of the terminal.
Negative number sets to max size based on terminal size.
.RE
\fBWAIT\fP
.RS
The number of milliseconds to wait for input (how fast the snake moves).
\fIDefault 231\fP.
\fI0\fP has no delay: the computer will play by itself (though you can still change directions).
\fI< 0\fP is drawing mode (makes \fIgetch()\fP block).
.RE
\fBEVADE\fP
.RS
The number of movements before the bug tries to move to a new place (evading the snake).
\fIDefault 300\fP (0 disables).
.RE
\fBWALLS\fP
.RS
Whether the snake can go through walls.
\fIDefault 1\fP (yes).
.RE
\fBCANNIBAL\fP
.RS
Whether the snake can go through itself.
\fIDefault 0\fP (no).
.RE
\fBSHED\fP
.RS
How many moves before the snake 'moults' (shedding of skin to make room for growth).
Again this isn't technically accurate in Snake.
\fIDefault 0\fP (disabled).
.RE
\fBSHEDS\fP
.RS
The amount to grow by each time the snake sheds its skin.
\fIDefault 5\fP.
.RE
.PP
The following two modify the terminal dimensions (this is not part of the game but the terminal/curses itself):
.PP
\fBLINES\fP
.RS
The following two modify the terminal dimensions (this is not part of the game but the terminal/curses itself).
Elsewhere I document these more and their potential pitfalls.
.TP
.B LINES
The number of rows.
Default is the maximum size of the current terminal size (\fBecho $LINES\fP will show the current value).
Please note that this is not the number of playable rows but the entire game itself.
.RE
\fBCOLUMNS\fP
.RS
.TQ
.B COLUMNS
The number of columns.
Default is the maximum size of the current terminal size (\fBecho $COLUMNS\fP will show the current value).
Please note that this is not be the number of playable columns but the entire game itself.
@@ -155,7 +148,7 @@ The markdown files have html counterparts; for some of these the html version is
.RS
Source file to the Snake game (IOCCC entry source file).
.RE
\fIremarks.markdown\fP
\fIREADME.md\fP
.RS
Describes what the entry (game) is, what it looks like, how to play etc.
Includes a variety of other information as well as NAQs (never asked questions that I believe could actually be asked so perhaps better would be infrequently asked questions, IAQs).
@@ -172,16 +165,16 @@ Information to help work out any possible problems as well as show how to config
.RS
A troubleshooting guide focusing more on terminals.
.RE
\fICOMPILING\fP
\fICOMPILING.md\fP
.RS
Information on Portability, C standards as well as (for the IOCCC judges) both Justifications for certain things and lints.
.RE
\fIHACKING\fP
\fIHACKING.md\fP
.RS
Documentation on how to modify the input keys, colours, wall, bug and snake chars (to print), the status/score line and various other things.
Includes feature ideas as well as how one might implement them.
.RE
\fIspoilers.markdown\fP
\fIspoilers.md\fP
.RS
Technical details that give more away about how the program works (\fIHACKING\fP also does a bit of this).
.RE
@@ -189,7 +182,7 @@ Technical details that give more away about how the program works (\fIHACKING\fP
.RS
To build the terminal capabilities test utility and the Snake game itself.
.RE
\fIsnake-colours\fP
\fIsnake-colours.sh\fP
.RS
Helper script to compile the game with player selected colours.
.RE
@@ -197,7 +190,7 @@ Helper script to compile the game with player selected colours.
.RS
List of bugs and things that might appear to be bugs that aren't, were once bugs etc.
.RE
\fIchocolate\-cake.markdown\fP
\fIchocolate\-cake.md\fP
.RS
The judges love chocolate and who can blame them? This is something of a speciality of my mum's but it's a delicious cake that even my late stepmum (who only liked white chocolate) loved! Double\-layer chocolate fudge cake.
.RE
@@ -210,13 +203,13 @@ Source file to test terminal support for the used capabilities the Snake game us
See this file to see the manual i.e. this file.
Don't get lost in the snake pit!
.RE
\fIcannibalism.log\fP
\fIcannibalism.log.md\fP
.RS
A screen dump of what cannibalism can look like right before winning.
.RE
\fIcrazy.log\fP
\fIcrazy.log.md\fP
.RS
A screen dump of the positive/push - negative/shrink mode.
Screen dumps of the positive/push - negative/shrink mode.
.RE
\fIplay.sh\fP
.RS
@@ -231,21 +224,16 @@ It also clears the screen before playing to try and maintain terminal sanity sho
.RE
.RE
.SH BUGS
.RS
\fB\-\fP The screen keeps showing bugs!
.RE
.RS
\fB-\fP There's no boss screen.
.RE
.RS
\fB\-\fP It was written by a human being.
.RE
.RS
\fB\-\fP Technically walls, snakeskin and the snake itself would \fBnot\fP be an obstacle in real life.
\fB\-\fP the screen keeps showing bugs!
.br
\fB\-\fP there's no boss screen.
.br
\fB\-\fP it was written by a human being.
.br
\fB\-\fP technically walls, snakeskin and the snake itself would \fBnot\fP be an obstacle in real life.
The shed snakeskin is not an obstacle but walls can be and so can the snake itself.
Also snakes grow by moulting (technical term for shedding) so when the snake leaves its snakeskin behind it really ought to grow but instead it shrinks; this means negative shedding ought to have the snake grow and positive shedding have the snake - what?
It's a misnomer: it took fewer bytes and the negative shedding is a positive consequence of unsigned arithmetic.
.RE
.PP
On a more serious note see the \fBbugs.md\fP file.
.SH EXAMPLES
@@ -300,4 +288,4 @@ For more gameplay information and modes check \fIgameplay.md\fP; and for an inte
Written by Cody Boone Ferguson for IOCCC 2020 (the 27th IOCCC).
.SH SEE ALSO
.PP
\fB\<https://ioccc.xexyl.net/2020/snake\>\fP The 27th IOCCC 'Don't tread on me award'.
\fB\<https://ioccc.xexyl.net/2020/snake\>\fP The 27th IOCCC 'Don't tread on me' award.
+62 -44
View File
@@ -38,10 +38,10 @@ already knew that so no harm there, hey?
The gameplay I have already described but this explains the way things are
actually done in more detail.
First I initialise curses; if this fails I print an error message ('curses
error') and return 1. Next the screen size is determined and I subtract 1 from
both x/y. If it's too small curses is ended and an error message ('screen
too small') is printed - returning 1.
First I initialise curses; if this fails I print an error message (`curses
error`) and return 1. Next the screen size is determined and I subtract 1 from
both `x`/`y`. If it's too small curses is ended and an error message (`screen
too small`) is printed - returning 1.
Next the max size of the snake is determined: if `MAXSIZE` is specified then
it's the value of that; otherwise it's the default. But in either case if it's
@@ -49,18 +49,18 @@ too big based on the screen dimensions it's capsized. The arrays for the snake
are this size + 1. Other environmental variables are also processed.
Then the arrays are allocated; if either return NULL curses is ended, an error
message ('memory error') is printed and return 1.
message (`memory error`) is printed and return 1.
If no errors have occurred I set up colours, turn on bold, set up the keypad
etc., place the snake, the bug, draw the walls and then it waits for the first
move (or quit of course). Technically I don't 'wait' for input as such: unless
the wait time is negative (drawing mode) it's just a non-blocking call up to the
WAIT milliseconds but in no event will the snake move until it is told to at
`WAIT` milliseconds but in no event will the snake move until it is told to at
least once (in drawing mode it will always wait).
In drawing mode if you pause then when you resume (hit space) the snake will
move the last direction it had been told to move - because the direction is set
and input was detected (the same goes for another key e.g. 'g' - just like in
and input was detected (the same goes for another key e.g. `g` - just like in
normal mode). But it will then wait again for input.
The snake function both draws the snake and updates the arrays; if the new size
@@ -85,9 +85,9 @@ loses/quits/wins) the movement proceeds; if the game hasn't started yet it will
jump to the top of the loop. If it's paused the same thing until it's resumed.
The rest of the loop can be described thusly.
It checks the current max y/x, draws the walls, bug, gets any input from
It checks the current max `y`/`x`, draws the walls, bug, gets any input from
keyboard, and then if there is movement checks for collisions; if current max
x/y are is/are smaller than original then it's automatically a game over from
`x`/`y` are is/are smaller than original then it's automatically a game over from
running into walls regardless of that feature status - this simplifies things
greatly (see below for more details).
@@ -99,7 +99,7 @@ it's also game over.
As the snake moves it will for each move grow by one up to the technical size.
This might at times look a bit different from other times but it's a visual
thing based on the internal data of the game and the features enabled for
thing based on the internal state of the game and the features enabled for
example shedding and shedding size.
Finally if the snake's new coordinates are on a bug a new bug is placed on the
@@ -128,13 +128,13 @@ for bugs.
# <a name="manual-automatic" href="#toc">Drawing (manual) and computer playing (automatic) modes</a>
How do these modes work? The `timeout()` function takes a signed int: if it's <
0 it waits indefinitely; if it's 0 it returns immediately even if there's no
input waiting (if input is there then it's used instead); and if it's > 0 it
How do these modes work? The `timeout()` function takes a signed int: if it's `<
0` it waits indefinitely; if it's `0` it returns immediately even if there's no
input waiting (if input is there then it's used instead); and if it's `> 0` it
waits that many milliseconds. Thus a negative value allows for complete control
over the snake's movement (speed and otherwise), a value of 0 means the snake
over the snake's movement (speed and otherwise), a value of `0` means the snake
will move as long as it's not paused etc. and otherwise it waits up to that many
milliseconds (which is how the 0 value comes into play).
milliseconds (which is how the `0` value comes into play).
# <a name="collision" href="#toc">Collision detection</a>
@@ -149,8 +149,8 @@ verifying if the snake head is at a place that's occupied by the snake, the wall
or the bug.
The bug does its best to avoid landing where the snake is but given that snakes
are very flexible and cunning who can tell what might happen? As I noted these
bugs are not bugs! :)
are very flexible and cunning who can tell what might happen? As I noted _these
bugs are not bugs! :)_
## <a name="cannibalcollision" href="#toc">Cannibal collision detection</a>
@@ -351,7 +351,7 @@ converted this:
(!P && (P = 1))||(P=0);
- Makes use of the different 'namespaces' to have variables and labels be the
- Makes use of the different namespaces to have variables and labels be the
same identifier. For example in main() there's a variable that also have the
same name label for `goto` (there used to be two pairs in main()).
@@ -361,7 +361,7 @@ two loops (a for() in the 'while()'), re-entering (as in starting over) a for()
loop and for dealing with errors.
- Why do I check for `Q || P` and then goto x only to then check for `!Q && !D`
to then goto x? Do I need to check Q at all? In fact I do but at a quick glance
to then `goto x`? Do I need to check `Q` at all? In fact I do but at a quick glance
it might be easy to miss.
- Uses an unnamed array (compound literal) for several different game variables.
@@ -437,10 +437,10 @@ dimensions. Now in the first call in main() I have the code and tests following:
offsets for every time I use negative offsets. But where's the fun in that?
:)
BTW: Some of these are not in the table of expressions in the HACKING file.
BTW: Some of these are not in the table of expressions in the HACKING.md file.
Also there might be some code in there that is no longer correct (it was
before I started obfuscation) though it might be able to give an idea.
Spoilers abound in that file (in addition to this file) but the HACKING file
Spoilers abound in that file (in addition to this file) but the HACKING.md file
states things that are no longer true: a way of obfuscation itself! Instead
of misleading comments it's misleading statements in the file that explains
what some of the things are for!
@@ -731,7 +731,7 @@ simply return E() and thus it's only called once. Similarly for `Q` I set it to
top of the code and then in main() assigned like: `l = ++(int) { 3 } ;` which
equates to 4. I think that's pretty cool!
- What is this? `for(I=0; A&&I<A-(A-A||A); ++I)` In fact the `A-(A-A||A)` is
- What is this? `for(I=0; A&&I<A-(A-A||A); ++I)`. In fact the `A-(A-A||A)` is
equivalent to `A-1`: `A` here is always > 0 so `A-A` is 0; however because `A`
is non-zero the expression `A-A||A` is equivalent to `0 || A` and since A is
non-zero we know that the value is `1` and thus it's `A - 1`! I quite like this
@@ -818,25 +818,29 @@ Another thing that reduced a lot of bytes - 34 to be exact - is using the
`mvvline()` and `mvhline()` ncurses functions rather than use three for loops to
draw the walls.
Much later on I made it so that the E() function returns 1; this was a clever
Much later on I made it so that the `E()` function returns 1; this was a clever
way to have what I already did have - return 1 if there's an error (i.e. memory
error or curses error or screen too small) but otherwise return 0 without having
to have a separate int and I could get rid of two `{}` pairs as well as add
another use for the E() function: it calls endwin() exactly once (even though
another use for the `E()` function: it calls endwin() exactly once (even though
when there isn't an error condition it can be called more than once) as well as
printing the final score - or else an error. And now it also allows for printing
an error and returning from main() at the same time! This also means that by
returning E() I am returning 1 but if the end of main() is reached 0 is
returning `E()` I am returning 1 but if the end of main() is reached 0 is
returned. In either case curses is ended.
One of these optimisations saved four bytes simply by taking advantage of the
value of a loop iterator after the loop. The loop is:
for (I = 0; A && I < A - 1; ++I)
```c
for (I = 0; A && I < A - 1; ++I)
```
However after the loop I had this:
A > 1 && q(n[A - 1],e[A - 1], 'o');
```c
A > 1 && q(n[A - 1],e[A - 1], 'o');
```
When I was considering moving that line to before the loop it occurred to me
that as long as `A > 0` then `A - 1 == I` after the loop. I still had to check
@@ -857,43 +861,54 @@ the problem.
First, the function that turns on colours looked like:
if (h) attron(m(PAIR(L)));
else attroff(m(PAIR(L)));
```c
if (h) attron(m(PAIR(L)));
else attroff(m(PAIR(L)));
```
But I thought is that else actually needed? If I were to switch the order so
that the `attroff()` is called first then I could just do an `if (h)` -
therefore getting rid of the `else`! But then I thought why not do what I've
done elsewhere? I had:
attroff(m(PAIR(L)));
if (h) attron(m(PAIR(L)));
```c
attroff(m(PAIR(L)));
if (h) attron(m(PAIR(L)));
```
But I then saved an extra byte by making it:
attroff(m(PAIR(L)));
h && attron(m(PAIR(L)));
```c
attroff(m(PAIR(L)));
h && attron(m(PAIR(L)));
```
That saved two bytes.
Second let's look at the B() function: there were two things I thought of that
Second let's look at the `B()` function: there were two things I thought of that
would save some bytes (I no longer know how many but a few at least possibly six
or even more). I had the following code:
if (V && U && o) { S(N); b(1); }
```c
if (V && U && o) { S(N); b(1); }
```
This made sure that only if the snake has caught a bug should it grow by the
growth size and increase the bug count. But was checking for the `V` or `U`
necessary? I gave it a look over and it occurred to me: the only time B() is
necessary? I gave it a look over and it occurred to me: the only time `B()` is
called with `1` (thus `o` is 1 so the if is true) was if the bug actually is
caught. That means I could remove the `V && U &&` part of the if! Thus it became
instead:
if (o) { S(N); b(1); }
```c
if (o) { S(N); b(1); }
```
Then this
!o && V && U && q U,V,' ');
```c
!o && V && U && q U,V,' ');
```
...is to make sure to only clear the spot the bug is (or was) at if
the bug was NOT eaten. Why not eaten? Because if it was eaten it means the
@@ -912,7 +927,9 @@ where the snake already is. So if I had to make sure the game had started (the
snake has moved at least once) what could I check? In fact that means the
direction is not 0! So I could just replace it with:
!o && D && q *U,*V,' ');
```c
!o && D && q *U,*V,' ');
```
That reduced the count further. But then I wondered if even that much is needed.
The condition is that the bug has evaded which means the snake has moved already
@@ -927,9 +944,11 @@ the score line is updated: so the empty space will be overwritten anyway. And if
the score line is made an empty string it doesn't matter because it'll appear as
all spaces anyway. Thus I could save another three bytes by changing it to:
!o && q *U,*V,' ');
```c
!o && q *U,*V,' ');
```
Technically it would be a `" "` (see [HACKING][] file) else for N (not referring to
Technically it would be a `" "` (see [HACKING.md][] file) else for N (not referring to
the variable though it might very well matter here too) movements (i.e. due to
sizes) you will at times (e.g. after eating a bug) see the snake body char at
0,0. I'm not bothered about that though because the score line is meant to
@@ -939,8 +958,7 @@ imposition on usage (and for that matter having a 'o' at 0,0 isn't either).
[gameplay.md]: gameplay.md
[gameplay.html]: gameplay.html
[COMPILING]: COMPILING
[HACKING]: HACKING
[HACKING.md]: HACKING.md
-----------------------------------------------------------------------------------------------------
(c) Copyright 1984-2020, [Leo Broukhis, Simon Cooper, Landon Curt Noll][judges] - All rights reserved
+2 -2
View File
@@ -232,7 +232,7 @@ properly. Because that's what funny means in this context.
It'd be nice if curses could detect hitting more than one arrow key at the
same time so that diagonal directions would be possible but unfortunately it's
not (`cat -v` confirms this). Even if I were to define four extra keys what
would they be? And what about the head character? The HACKING file has
would they be? And what about the head character? The [HACKING](HACKING.md) file has
information on this.
I also limit the terminal size to 10 lines/columns but most likely it would
@@ -255,7 +255,7 @@ from a quick test this holds even with a terminal that has a white background.
There is a way to force the background to remain white though that's by way of
monochrome (so no colours); I discuss that and another related terminal specific
in the troubleshooting guide (there's another way but it would take me over the
iocccsize; I talk about it in the HACKING file).
iocccsize; I talk about it in the [HACKING](HACKING.md) file).
## <a name="monochrome" href="#toc">Monochrome terminals</a>
+58 -39
View File
@@ -37,7 +37,7 @@ it as structured as possible/easiest to find.
Because the score line is dynamic in length depending on the terminal size it
can happen that the score/status wraps to the next line(s). This can be fixed by
modifying the status line (see [HACKING][]) but you shouldn't need it to be very
modifying the status line (see [HACKING.md][]) but you shouldn't need it to be very
wide.
* RESOLUTION #0: Use a bigger screen (it doesn't take that many columns)
@@ -59,7 +59,7 @@ wide.
#define J Z(B:%zu\n), c);
Recompile and try again. That will then show only the number of bugs eaten. See
the HACKING file for other data you can include (though there are some
the [HACKING.md][] file for other data you can include (though there are some
discrepancies here and there as you'll see).
@@ -210,18 +210,18 @@ snake. See below for some tests.
terminal supports bold
terminal supports colours
terminal rows 42 (39 playable)
terminal cols 157 (155 playable)
terminal rows 41 (38 playable)
terminal cols 154 (152 playable)
snake size: 997 (max size: 6006)
bugs: 199 (max size: 1201)
snake size: 997 (max size: 5738)
bugs: 199 (max size: 1147)
at least 34 columns recommended for snake size 997 (is 157)
at least 37 columns recommended for snake size 6006 (is 157)
at least 34 columns (currently 154) recommended for snake size 997
at least 37 columns (currently 154) recommended for capped snake size 5738
No problems detected.
Why are there only 39 playable lines for 42 rows? The first row is the score
Why are there only 38 playable lines for 41 rows? The first row is the score
line, the second is the top wall, the last is the bottom wall. For columns
you have the left and rightmost columns for the walls.
@@ -286,7 +286,8 @@ so that's probably why; in CentOS I did have to install it specially.
The `-m` suffix to terminal names means monochrome. Under Linux this
terminal is under `/usr/share/terminfo/l/linux-m` and under macOS (Catalina
at least) it's at `/usr/share/terminfo/6c/linux-m`.
at least) it's at `/usr/share/terminfo/6c/linux-m` (for macOS Ventura it's
`/usr/share/terminfo/6c/linux-m`).
* BOTTOM LINE: Try using a monochrome terminal for the game by e.g.:
@@ -295,7 +296,7 @@ so that's probably why; in CentOS I did have to install it specially.
And then on your white background terminal the game should keep the
background white and the foreground black. This will of course mean you have
no colour but unfortunately changing the background colour of curses would
eat too many bytes in the entry (I cite the functions in the HACKING file).
eat too many bytes in the entry (I cite the functions in the [HACKING.md][] file).
I discuss colours and terminals in much more detail in
[terminals.md][] ([terminals.html][]).
@@ -335,7 +336,9 @@ hit a key every time to move because it is blocking.
What happens if you do:
WALLS= ./prog
```sh
WALLS= ./prog
```
Can you run into the wall since you didn't specify a value and the default is 1?
No. That's not the way it works. By saying WALLS= you have told the system that
@@ -347,43 +350,55 @@ for a number could have a better value than 0.
If however you were to give valid input followed by invalid input the valid
input would be parsed. For example:
WALLS=1test ./prog
```sh
WALLS=1test ./prog
```
Would let you go through the walls. On the other hand
would let you go through the walls. On the other hand
WALLS=test1 ./prog
```sh
WALLS=test1 ./prog
```
Would not. Furthermore if you specify the same variable more than once it's the
would not. Furthermore if you specify the same variable more than once it's the
last one that counts. For example:
WALLS=0 WALLS=1 CANNIBAL=1 CANNIBAL=0 ./prog
```sh
WALLS=0 WALLS=1 CANNIBAL=1 CANNIBAL=0 ./prog
```
Would allow you to go through walls but you could not cannibalise.
would allow you to go through walls but you could not cannibalise.
Also note that I specify base 0 which means that you can also have it in hex and
octal. These are all the same:
SIZE=0x3
SIZE=3
SIZE=03
```
SIZE=0x3
SIZE=3
SIZE=03
```
The way the functions work is if 0x (or 0X) it's considered hex; if prefixed
with a 0 it's octal and otherwise it's decimal. If you specify invalid data
(even if only at the beginning of the string) the functions return 0. For
example
example:
SIZE=0
SIZE=f
SIZE=-
SIZE=f1
```
SIZE=0
SIZE=f
SIZE=-
SIZE=f1
```
Are all equivalent.
are all equivalent.
Observe that
Observe that:
SIZE=08
```
SIZE=08
```
Is invalid input because octal only has digits 0 - 7: the resulting size will be
is invalid input because octal only has digits 0 - 7: the resulting size will be
0.
@@ -402,31 +417,35 @@ screen size must be at least 10 lines/columns (though in code the number will
not be 10); if either is smaller you will get 'screen too small' and the game
will end (yes things that haven't begun can be ended).
If calloc() fails to obtain the needed memory you'll see something like (here it
If `calloc()` fails to obtain the needed memory you'll see something like (here it
was before I added proper - to make the perfect and obvious pun - capsizing
so that by setting the size to -1 it went to the max unsigned value):
$ MAXSIZE=-1 ./prog
memory error
X:0/157 Y:0/42 S:3/18446744073709551614 B:0
```sh
$ MAXSIZE=-1 ./prog
memory error
X:0/157 Y:0/42 S:3/18446744073709551614 B:0
```
BTW: There are two arrays that have to be **MAXSIZE** (technically + 1).
BTW: There are two arrays that have to be **`MAXSIZE`** (technically + 1).
Is it possible that some value specified by the user could mess this up? I do
not know but what I do know is that because it's unsigned it can't be negative;
if the max size is 0 then the array size will be 1 but it won't matter because
the snake size will be >= that max anyway. But here's a curious output:
SIZE=1 MAXSIZE=0 ./prog
YOU WIN!
X:78/156 Y:20/41 S:0/0 B:0
```sh
SIZE=1 MAXSIZE=0 ./prog
YOU WIN!
X:78/156 Y:20/41 S:0/0 B:0
```
Why does it show snake size as 0 when I specified 1? It's because the max size
is 0 and I limit the size to be no bigger than the max size.
[HACKING]: HACKING
[HACKING.md]: HACKING.md
[prog.c]: prog.c
[bugs.md]: bugs.md
[terminals.md]: terminals.md