diff --git a/2020/ferguson1/COMPILING.md b/2020/ferguson1/COMPILING.md index 2ac12b5a4..d0c2f8e3b 100644 --- a/2020/ferguson1/COMPILING.md +++ b/2020/ferguson1/COMPILING.md @@ -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 diff --git a/2020/ferguson1/HACKING.md b/2020/ferguson1/HACKING.md index 4b4e19e29..e75791a79 100644 --- a/2020/ferguson1/HACKING.md +++ b/2020/ferguson1/HACKING.md @@ -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 + 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 diff --git a/2020/ferguson1/Makefile b/2020/ferguson1/Makefile index cd48b62d4..a1663a1eb 100644 --- a/2020/ferguson1/Makefile +++ b/2020/ferguson1/Makefile @@ -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 diff --git a/2020/ferguson1/README.md b/2020/ferguson1/README.md index 405a57b74..617b0063a 100644 --- a/2020/ferguson1/README.md +++ b/2020/ferguson1/README.md @@ -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 diff --git a/2020/ferguson1/bugs.md b/2020/ferguson1/bugs.md index f700ba5e2..d8049f3be 100644 --- a/2020/ferguson1/bugs.md +++ b/2020/ferguson1/bugs.md @@ -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 +``` # 'I was a few places behind my tail and I ran into it!' @@ -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`: - It looks like the bug is in the snake -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: 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 diff --git a/2020/ferguson1/crazy.log b/2020/ferguson1/crazy.log.md similarity index 89% rename from 2020/ferguson1/crazy.log rename to 2020/ferguson1/crazy.log.md index d23e96418..eb88e46cf 100644 --- a/2020/ferguson1/crazy.log +++ b/2020/ferguson1/crazy.log.md @@ -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 ########################################################################################## # # # 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 ########################################################################################## # # # 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 ########################################################################################## # 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 diff --git a/2020/ferguson1/gameplay.md b/2020/ferguson1/gameplay.md index f8ba41d0a..bde91d532 100644 --- a/2020/ferguson1/gameplay.md +++ b/2020/ferguson1/gameplay.md @@ -78,11 +78,11 @@ For troubleshooting other problems see [troubleshooting.md][] # 2. Input keys -- 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: ## Gameplay: Playing the game - 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. # Gameplay: Shedding (stretching) -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). # Gameplay: Winning -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. ## Photosensitive epilepsy/Stimulation overload trigger warnings 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> + ## Gameplay mode: Computer playing / Automatic mode (Photosensitive epilepsy/Stimulation overload trigger) @@ -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 +``` ## Gameplay mode: Expansibility @@ -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. Notes about some of the game variables @@ -750,7 +821,7 @@ snake to move slower you have to increase the wait time! ## EVADE: Bug evasion frequency -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 ----------------------------------------------------------------------------------------------------- diff --git a/2020/ferguson1/snake.1 b/2020/ferguson1/snake.1 index 0f685f87e..9638910da 100644 --- a/2020/ferguson1/snake.1 +++ b/2020/ferguson1/snake.1 @@ -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\\fP The 27th IOCCC 'Don't tread on me award'. +\fB\\fP The 27th IOCCC 'Don't tread on me' award. diff --git a/2020/ferguson1/spoilers.md b/2020/ferguson1/spoilers.md index 03254de0e..97eda191a 100644 --- a/2020/ferguson1/spoilers.md +++ b/2020/ferguson1/spoilers.md @@ -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. # Drawing (manual) and computer playing (automatic) modes -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). # Collision detection @@ -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! :)_ ## Cannibal collision detection @@ -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 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 diff --git a/2020/ferguson1/terminals.md b/2020/ferguson1/terminals.md index 914869f63..562bb7d84 100644 --- a/2020/ferguson1/terminals.md +++ b/2020/ferguson1/terminals.md @@ -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). ## Monochrome terminals diff --git a/2020/ferguson1/troubleshooting.md b/2020/ferguson1/troubleshooting.md index 81498a2e8..0b7f1de66 100644 --- a/2020/ferguson1/troubleshooting.md +++ b/2020/ferguson1/troubleshooting.md @@ -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