From 8b217ac843defe6a79c34ab11d6e7bd476c3564b Mon Sep 17 00:00:00 2001 From: Cody Boone Ferguson <53008573+xexyl@users.noreply.github.com> Date: Thu, 21 Dec 2023 08:25:00 -0800 Subject: [PATCH] Updates to 2020/ferguson1 Updated chocolate-cake.md file with some added notes. Added PDF file ioccc-and-chocolate.pdf which was downloaded from a website I originally linked to in chocolate-cake.md. Removed html files based on the markdown files as the markdown files are the source of them and this is being done differently now. In other words index.html and inventory.html have not been removed, just those that were created by me (or from what I wrote). This does not format fix anything as I can always do that later. It is after all my entry. Minor fix in play.sh. Minor update in bugs.md. I believe this completes the review of 2020/ferguson1 but it's entirely possible (almost certain) that things could be cleaned up more. However in order to get through the rest of 2020 I am only doing quick passes over my entries. --- 2020/ferguson1/bugs.html | 334 --------- 2020/ferguson1/chocolate-cake.html | 203 ------ 2020/ferguson1/chocolate-cake.md | 28 +- 2020/ferguson1/gameplay.html | 838 --------------------- 2020/ferguson1/ioccc-and-chocolate.pdf | Bin 0 -> 46195 bytes 2020/ferguson1/play.sh | 2 +- 2020/ferguson1/spoilers.html | 970 ------------------------- 2020/ferguson1/spoilers.md | 53 +- 2020/ferguson1/terminals.html | 394 ---------- 2020/ferguson1/troubleshooting.html | 474 ------------ bugs.md | 2 +- thanks-for-help.md | 9 +- 12 files changed, 53 insertions(+), 3254 deletions(-) delete mode 100644 2020/ferguson1/bugs.html delete mode 100644 2020/ferguson1/chocolate-cake.html delete mode 100644 2020/ferguson1/gameplay.html create mode 100644 2020/ferguson1/ioccc-and-chocolate.pdf delete mode 100644 2020/ferguson1/spoilers.html delete mode 100644 2020/ferguson1/terminals.html delete mode 100644 2020/ferguson1/troubleshooting.html diff --git a/2020/ferguson1/bugs.html b/2020/ferguson1/bugs.html deleted file mode 100644 index d2ee86395..000000000 --- a/2020/ferguson1/bugs.html +++ /dev/null @@ -1,334 +0,0 @@ - - - - - - - - -
- IOCCC -

The International Obfuscated C Code Contest

-
- -
-

Supplemental Info

-
- - -

Current bugs and things that look like bugs but are not, as of 12 September 2020.

- -

- - -

Built-in test unit for some features!

- -

Fun fact: There’s something of a built-in test unit for some features; much -like some of the other features this was not intentionally designed but is -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
-
- -

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.

- -

If you set it to SIZE=10 there would be a difference of 10. Now every -move you will see the size the snake is. But then…Why is it that it -doesn’t seem to move until the snake is of size 20? Because you start out at -size 10 of course (but in fact if you observe the snake won’t appear to be -of size 20 yet).

- -

There probably are combinations that can test these types of things I just -don’t at this time remember any I might have done.

- -

The snake doesn’t seem to be moving

- -

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
-
- -

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’. -When it has reached the full size then the entire body of the snake will -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 -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 -be a moment of lingering where it appears the snake moves for a few places -before it then stops moving and just grows until it’s the full size.

- -

It can also appear to be not moving when the snake is extraordinarily long -and more so when the snake has just eaten: which of course is when it would -grow (just like when shedding). Even so sometimes it takes a moment for the -snake tail to catch up with the body (so to speak) so that it could for a -few moments be difficult to know if where you’re going is going to be -occupied or not.

- -

Basically if the snake head is moving you can consider the snake to be -moving or growing and that’s the important point (if you have the default -status line you can also see the coordinates being updated to show this).

- -

getch() also accepts input from some input hardware other than keyboards

- -

On my MacBook Pro the trackpad has actually triggered directions; if I were to -swipe up and then down the snake would run into itself. I believe this has to do -with the function call keypad(v, 1); which enables arrow keys.

- -

SIZE=0 is the same as SIZE=1 and other oddities

- -

What this means is that for both you will only have the snake head. At this point -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 -back to the maximum so you will win automatically (this is the same as the -second mode the judges suggested with SIZE=-1 ./prog).

- -

As for the growth sizes (including shedding) if it’s < 0: the snake will still -lose its length and display will still be odd: because the snake size is -decreased any element beyond the reduced size isn’t touched so the snake -characters will remain on the screen; only the moving snake is what matters. -But it’s likely the game will be won fairly quickly because once it’s -1 in size -it’ll be at the maximum size.

- -

For example if I say SIZE=3 GROW=-2 at two bugs the game is won.

- -

As for growing: anything > 0 it will grow that many; 0 it will not grow (which I -show as a gameplay mode); and < 0 it will ‘shrink’ as I explained above. You could -say that it’s defying physics by growing and shrinking simultaneously but either -way negative size will force a win.

- -

Here’s a fun output:

- -
        < <<<+
-
- -

That’s from:

- -
        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
-
- -

Try this though:

- -
        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 -being unsigned). This isn’t a bug even if it might seem like one. In fact -this is the basis of the grow/shrink mode of play that I talk about in the -gameplay.md (gameplay.html) file.

- -

Here’s another thing to be aware of. Let’s say you do:

- -
        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
-
- -

‘I was a few places behind my tail and I ran into it!’

- -

I.e. what happens if you’re growing - for example after just eating a bug - and -you’re racing towards your tail? If you’re behind it should be fine since you’re -going the same speed as the snake is, right? Not quite. Because you’re growing -you might end up running into your tail (depending on how much you’ve left to -grow and how far away from your tail)! This happened to me once and it surprised -me and I figured I better note it.

- -

Remember: the tail at that point isn’t moving yet; the snake is growing!

- -

With negative growth the snake drops from the middle of the body

- -

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>
-             ^
-             >
-
- -

The ^ points to the part that’s split from the snake and which will be -there on its own after the snake passes through it. Here’s an example with -it being SHEDS=-3:

- -
        <ooo<ooooo
-        ^^^
-        <oo
-
- -

If you go through the part that has split (which for some might be difficult -to see whilst the snake has not fully left its skin) you won’t run into -yourself; if you run into another part though you do. This might be a bit -confusing I know but what kind of animal shrinks in size by leaving its head -behind only to sprout up a new head? Actually…on second thought, I won’t -go there! :)

- -

Anyway it’s an odd idea for the snake to shrink by leaving part of its skin: -that’s how they grow in the wild so it’s a misnomer. The shrinking was only -‘discovered’ because it’s an unsigned number but happily it allows for different -gameplay modes.

- -

It looks like the bug is in the snake

- -

I show a different example in the crazy.log 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
-
- -

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.

- -

On the safety of the bug placement loop

- -

Ilya Kurdyukov shared concern for the loop in the bug placement routine: is it -safe? What he refers to is that it’s a potential infinite loop. But is it? I -understand the concern and it’s something I played with a lot to be sure that it -doesn’t happen. This is how it works.

- -

To explain it I will go back to the make test program termcaps (which is -imperfect for the recommended terminal size but the max snake size is exactly -how it’s done in Snake and works well). The minimum terminal size I require in -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
-
-    terminal rows  10 (7   playable)
-    terminal cols  10 (8   playable)
-
-    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)
-
-    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 -(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 -very poor implementation of rand() and I discuss this in -troubleshooting.md (troubleshooting.html).

- -

There I also give some test outputs without the cap in place to give an idea of -when it became a problem (or would have). In short this is known but I do not -think it will be a problem and I have given resolutions. In any event without -cannibal mode it’s even less likely that you’ll ever run into this problem.

- -

The obvious question is why didn’t I put in a break? Because of the IOCCC size -rule 2 and what would it do anyway? I guess it would win but to my mind that is -a bug and I prefer putting in restrictions to try and make it very unlikely.

- -

Vertical snake has more space between snake char than horizontal snake

- -

Ilya Kurdyukov also pointed out that depending on orientation of the snake there -are different amount of spaces between the snake char ‘o’. This is what he -had to say on it:

- -

Yes, it is the problem, space in character terminal is not isotropic -from a viewer (gamer) perspective so one step up look like two steps -right.

- -

MATE Terminal 1.20.0, and font “Ubuntu Mono Regular 13”

- -

But, as I said, there is no terminal problem itself, it’s because -character tiles are not square (except for Japanese/Chinese, Japanese -even have so called full-width English characters in their encoding, -presented in UTF too).

- -

https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms

- -

So the typical fonts are Half-width or close to 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 -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 -the source code layout too much - to say nothing else of the fact I have -also forgotten some of what I did obfuscation wise (and the -spoilers aren’t enough here!).

- -

That being said the font and font size does seem to have somewhat of an -effect on it since his screenshots have wider gaps than mine.

- -

Once again thank you mate for bringing this to my attention and also playing -with it and providing the resources! Much appreciated! (And although you -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.)

-
-
- - - - -
Creative Commons License

© Copyright 1984-2020, -Leo Broukhis, Simon Cooper, Landon Curt Noll -- All rights reserved
-This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

 
- - diff --git a/2020/ferguson1/chocolate-cake.html b/2020/ferguson1/chocolate-cake.html deleted file mode 100644 index 2c842761e..000000000 --- a/2020/ferguson1/chocolate-cake.html +++ /dev/null @@ -1,203 +0,0 @@ - - - - - - - - -
- IOCCC -

The International Obfuscated C Code Contest

-
- -
-

Supplemental Info

-
- -

Double-layered Chocolate Fudge Cake

- -

Here's a delicious double-layered chocolate fudge cake that is -something of my mum's speciality. It's complicated and I should warn that nobody -has seemed to get it down as well as she has but it's well worth the effort if -you love chocolate. My late stepmum who only liked white chocolate loved this -cake!

- -

picture of the double-layered chocolate fudge cake

- -

There are two sets of ingredients and instructions; the cake and the icing. In -addition there are some notes to pay attention to; some will be repeated because -they're really important for the quality of the cake.

- -

Below I have included the ingredients separately but I include them within the -instructions as well and in bold. Also in bold are some of the special notes.

- -

Finally note that depending on where you are in the world this might not work in -the warmer months of the year for the icing in particular. Oh and here's an -amusing fact about the IOCCC and chocolate. Did I say 'the IOCCC'? I actually -meant 'an IOCCC': it stands also for 'International Office of Cocoa, Chocolate -and Sugar Confectionery'. Perfect.

- -

See also the International Original Chocolate Cake Contest tweet from -fellow IOCCC winner Ilya Kurdyukov (GitHub) as well as the -IOCCC cake source image of the picture in the tweet (which he painted over -my photo of the cake with GIMP).

- -

Measurements

- -

This was originally from an American cookery book so the measurements are in the -US standards. A nice resource (that has much more than just cooking) is -http://www.onlineconversion.com/cooking.htm. This will particularly matter -for cups, teaspoons, tablespoons and ounces.

- -

Baker's chocolate

- -

Baker's chocolate is a brand name and the product in question is unsweetened -chocolate bars used for baking. According to a chocolates FAQ it might be -called different things:

- -
Bitter chocolate contains no sugar and is often called 'unsweetened' or
-'baking' chocolate. In some countries it's called 100% cacao since it's
-composed only of ground up cocoa beans, and nothing else.
-
- -

The FAQ suggests that you might have to look for a baking supply shop but I -cannot offer any specific advice other than it's unsweetened chocolate bars for -baking. The FAQ talks about what looks like substitutes but I have no idea what -that would turn out as; I somehow suspect it wouldn't be good.

- -

Either way please still pay attention to the notes on the chocolate in the recipe and -recipe notes because they're really important!

- -

Special notes for the cake

- -

PAY CLOSE ATTENTION TO THESE:

- - - - -

Cake Ingredients

- - - - -

Cake Preparation

- -
    -
  1. Lightly grease two 9 x 1.5 inch round pans; lightly dust with flour.

  2. -
  3. Preheat oven to 350 F (176.67 C).

  4. -
- - -

Cake Directions

- -
    -
  1. Melt 3 oz unsweetened chocolate in double boiler or small bowl over -hot (but not boiling) water; let cool whilst proceeding.

  2. -
  3. Sift 2 1/4 cups flour, 2 tsp baking soda, and 1/2 tsp salt

  4. -
  5. Beat 1 stick margarine in large bowl until soft.

  6. -
  7. Add 2 1/4 cups packed brown sugar and 3 eggs. Beat until light and -fluffy.

  8. -
  9. Beat in 1 1/2 tsp vanilla and cooled melted chocolate.

  10. -
  11. Stir in dry ingredients alternately with 1 cup sour cream; always beat -until smooth.

  12. -
  13. Stir in 1 cup boiling water.

  14. -
  15. Pour into prepared pans.

  16. -
  17. Bake for ONLY 35 minutes (NO LONGER) at 350 F (176.67 C).

  18. -
  19. When you take the cake out put the cake pans on a cooling rack for TWO -MINUTES ONLY AND THEN IMMEDIATELY TURN CAKES OUT ON TO RACK. Let cool -completely.

  20. -
- - -

Icing Ingredients

- - - - -

You will also need a bigger pan or bowl that can have ice water that the icing -bowl can sit in.

- -

Special notes for the icing

- -

PAY CLOSE ATTENTION TO THESE:

- - - - -

Icing Directions

- -
    -
  1. Combine 4 oz unsweetened chocolate and 1/2 cup margarine in pan. -Melt and remove from heat. DO NOT LET IT GET SOLID.

  2. -
  3. Combine 3 1/4 cups powdered sugar, 1/2 cups whole milk and 2 tsp -vanilla into a bowl. Stir until smooth.

  4. -
  5. Add chocolate mixture.

  6. -
  7. Set bowl in pan/bowl of ice and water. Beat with spoon/mixer until it's -thick BUT not too thick. Make sure icing doesn't get wet! The idea is to -MAKE THE BOWL IT'S IN COLD but not let the icing get wet.

  8. -
- - - - - -
Creative Commons License

© Copyright 1984-2020, -Leo Broukhis, Simon Cooper, Landon Curt Noll -- All rights reserved
-This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

 
- - diff --git a/2020/ferguson1/chocolate-cake.md b/2020/ferguson1/chocolate-cake.md index f464165cd..72be853aa 100644 --- a/2020/ferguson1/chocolate-cake.md +++ b/2020/ferguson1/chocolate-cake.md @@ -3,7 +3,7 @@ Here's a delicious double-layered chocolate fudge cake that is something of my mum's speciality. It's complicated and I should warn that nobody has seemed to get it down as well as she has but it's well worth the effort if -you love chocolate. My late stepmum who only liked white chocolate loved this +you love chocolate. My late stepmum who only liked white 'chocolate' loved this cake! ![picture of the Double-layered chocolate fudge cake](cake.jpg) @@ -69,21 +69,23 @@ recipe notes because they're really important!** note the amount is by ounce not squares as listed. See the measurements link if there's any doubt. -* Pay careful attention to **COOK FOR ONLY THE INDICATED TIME**. +* Pay careful attention to **COOK FOR ONLY THE INDICATED TIME**. Note that if + your oven cooks a bit hotter you might have to adjust the timing a bit. This + is likely the case if the cake comes out a little bit dry. -* **REMOVE THE CAKE FROM THE PANS 2 MINUTES AFTER REMOVING FROM OVEN**. Then allow - to fully cool on racks. The important point is that they don't overcook. +* **REMOVE THE CAKE FROM THE PANS 2 MINUTES AFTER REMOVING FROM OVEN** and + then allow to **FULLY COOL *ON RACKS*!** The important point is that they + don't overcook. * When the recipe was first written they sold powdered sugar in a box; now it's confectionery sugar in a bag. My mum experimented with the amount and found that 4 cups seems about right. -* She uses **WHOLE MILK** for this recipe; she doesn't know if it really matters - but given that she and I both prefer skim milk except for here and in - chocolate milk I would recommend whole milk for this recipe too. In any case - it turns out great **IF YOU FOLLOW *EVERYTHING* TO A T!** +* Use **WHOLE MILK**. It's just better for something like this and skim milk + or low fat milk is completely untested. -* Use only **PURE VANILLA EXTRACT**! Do **NOT USE imitation vanilla!** +* Use only **PURE VANILLA EXTRACT**! Do **NOT USE imitation vanilla!** Not + following this rule will lead to a worse outcome. ## Cake Ingredients @@ -161,7 +163,11 @@ bowl can sit in. * Use **WHOLE MILK.** -* Use **ONLY PURE VANILLA EXTRACT**! **DO NOT USE IMITATION VANILLA!** +* Use **ONLY PURE VANILLA EXTRACT**! **DO NOT USE IMITATION VANILLA!** Not + following this rule will lead to a worse outcome. As I recall the icing was + quite a bit worse (but still good) the times we had imitation vanilla. Just + don't do it. + ## Icing Directions @@ -181,7 +187,7 @@ bowl can sit in. [picture]: cake.jpg [http://www.onlineconversion.com/cooking.htm]: http://www.onlineconversion.com/cooking.htm -[IOCCC and chocolate]: http://www.gomc.com/firstpage/200103068.pdf +[IOCCC and chocolate]: ioccc-and-chocolate.pdf [International Original Chocolate Cake Contest tweet]: https://web.archive.org/web/20200805053007/https://twitter.com/ilyakurdyukov/status/1290520266087571458 [fellow IOCCC winner Ilya Kurdyukov GitHub]: https://github.com/ilyakurdyukov [GitHub]: https://github.com/ilyakurdyukov diff --git a/2020/ferguson1/gameplay.html b/2020/ferguson1/gameplay.html deleted file mode 100644 index fa6ca3b9e..000000000 --- a/2020/ferguson1/gameplay.html +++ /dev/null @@ -1,838 +0,0 @@ - - - - - - - - -
- IOCCC -

The International Obfuscated C Code Contest

-
- -
-

Supplemental Info

-
- - -

Playing the game is very simple but because there are many different modes I -have included much more than just the input keys and a few printouts of the -game. For normal play you probably don’t even need this file at all unless it be -for the input keys.

- -

- -
    -
  1. Testing terminal environment/capabilities

  2. -
  3. Input keys

  4. -
  5. Gameplay

    - - -
  6. -
  7. Gameplay modes

    - - -
  8. -
  9. Scripts

    - - -
  10. -
  11. Notes about some of the game variables

    - - -
  12. -
  13. Conclusion (and inspirational words for everyone)

  14. -
- - -

1. Testing terminal environment/capabilities

- -

Most people probably won’t have any issue at all but for those who have any -problems with the terminal see the terminals.md file for more -information on the terminal capabilities testing utility I include.

- -

For troubleshooting other problems see troubleshooting.md -(troubleshooting.html) and bugs.md (bugs.html).

- -

2. Input keys

- - - - -

3. Gameplay

- -

Here I will show the initial screen as well as what a few things look like. If -you don’t feel you need this but you want to configure colours jump to colour -selection. You can also jump to gameplay modes or game -variables.

- -

The bug colour by default is white; the snake head colour is red and the snake -body colour is green. The walls are cyan.

- -

Gameplay: The initial screen

- -

When you load the game you will see a screen that looks something like the -below. Note that the top line is the score/status line: the top wall is at line -1 and the left wall is column 0. Below I use the coordinates in the X/Y order -(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
-    ##################################################
-    #                                                #
-    #                                                #
-    #                                                #
-    #                                                #
-    #                                                #
-    #                                                #
-    #                       +                        #
-    #                                                #
-    #                                      *         #
-    #                                                #
-    #                                                #
-    #                                                #
-    #                                                #
-    #                                                #
-    ##################################################
-
- - - - -

Gameplay: Playing the game

- -
    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         #
-    #                                      *         #
-    #                                                #
-    #                                                #
-    #                                                #
-    #                                                #
-    #                                                #
-    ##################################################
-
- -

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         #
-    #                                                #
-    #                                                #
-    #                                                #
-    #                                                #
-    ##################################################
-
- -

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.

- -

Gameplay: Walls

- -

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            #
-    #                                                #
-    #                                                #
-    #                                                #
-    #                                                #
-    #                                   ^            #
-    ##################################################
-
- -

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            #
-    ##################################################
-
- -

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.

- -

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                        #
-    #                                                #
-    #                                                #
-    #                                                #
-    #                                                #
-    #                                                #
-    #                                                #
-    #                                                #
-    ##################################################
-
- -

If I continue going up I will see:

- -
    OUCH!
-    X:20/49 Y:1/16 S:150/658 B:0
-
- -

Gameplay: Cannibalism

- -

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 #
-    #                                                #
-    #                                                #
-    #                                                #
-    #                                                #
-    ##################################################
-
- -

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
-
- -

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       #
-    ##################################################
-
- -

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       #
-    ##################################################
-
- -

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       #
-    ##################################################
-
- -

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.

- -

An example of extreme cannibalism is in the cannibalism.log file. It has -more than one run the first of one has several dumps. I didn’t move for a long -time: the initial size was 1500 and I would have won before moving if I went -after bugs right away. In the last two captures of the first invocation (the -third and second to last dump) the bug, head and tail of the snake are at:

- -
     Bug: line 87, col 47
-    Head: line 88, col 62; and line 115, col 48
-    Tail: line 84, col 46; and line 112, col 29
-
- -

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 -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 -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
-
- -

Observe that the size isn’t the default 997: because the screen size is too -small for that value (see terminals.md).

- -

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).

- -

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 -won? Very few probably do and those who do enjoy them it probably are games that -are relaxing and stimulating (which admittedly this game can be - but still I -want it to be winnable too).

- -

Another reason I chose 997: it’s prime and the judges love prime numbers. -Actually, this is the main reason for the number: I had changed it down to 1000 -but then it occurred to me that there are prime numbers close to 1000 so why not -choose one instead (that was less than 1000)? And so I did. Another fun fact: at -the default starting size and growth size of 5 the number of bugs needed before -the game is won is 199 - also a prime!

- -

Gameplay: Quitting

- -

If you wish to quit you can hit ‘q’ (case-insensitive). For example:

- -
    X:24/49 Y:8/16 S:5/658 B:0
-
- -

4. Gameplay modes

- -

Here I list a number of different modes of play. In the bugs.md -(bugs.html) file I even show how there’s a built-in test unit for some -modes! There are certainly other modes that could be imagined: the modes below -happened over time and some were simply an accidental discovery or something I -thought of by a chance; others were thought out from the beginning but were -improved over time.

- -

The play.sh script sets up some of these automatically; I describe it after -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) -per second triggering seizures is wide but depending on the variables it -easily can be in the range.

- -

There are different patterns and lights flashing at different speeds so please -exercise caution. This could trigger seizures and it might also be too much -stimulation for some people.

- -

Please be careful of these if these types of things have ever affected you or if -you have any form of epilepsy. Thank you.

- -

Gameplay mode: Drawing / Practice mode

- -

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
-
- -

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
-
- -

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
-
- -

Do note that although you’re a bigger size you still grow one movement at a -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
-
- -

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
-
- -

Which I’ve done in the play.sh script.

- -

I discuss how this mode works in spoilers.markdown.

- -

Here’s an example run where I spelt ‘SNAKE’. As you can see it’s far from -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>
-
- -

Gameplay mode: Computer playing / Automatic mode (Photosensitive epilepsy/Stimulation overload trigger)

- -

Photosensitive epilepsy/stimulation overload trigger warning: -For those who have epilepsy or are susceptible to stimulation overload from -flashing lights please do be careful with this mode. Depending on your -susceptibility this could very well trigger seizures or other discomforts and I -wouldn’t want that to happen to anyone. This mode has a lot of flashing of -different colours and depending on the exact parameters you specify it could -easily be in the trigger range for epilepsy but potentially stimulation overload -too.

- -

If you’re still here you can create this effect like:

- -
    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 -can still move the snake though it won’t seem like you have much control over -it. If you pause the game you can see that it’s bigger (you probably will be -able to even without pausing it).

- -

Basically the snake is moving so fast that because the bug evades the bug will -inevitably move in the snake path’s and therefore be eaten.

- -

You might find it goes quicker if you set the evade count to a lower value for -example 1. Or you might not. But as long as the bug moves the computer can play -by itself.

- -

Quick tests suggest that bigger dimensions it gets bigger quicker. But it also -seems to take longer for it to get a bug as it gets longer. I believe these -happen because the bug has to find a X and Y coordinate that’s not occupied and -so with fewer available it has a harder time finding a place in that one line.

- -

Computer player variations

- -

Here’s how to make the bug move every time the snake moves:

- -
    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
-
- -

Negative growth (Shrink) Automatic mode

- -

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
-
- -

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 -get to that length - and then it’ll start shrinking until it’s just the head -then the head again (i.e. 1 then 0 in size) and then win! This is also in the -play.sh script.

- -

Adjusting the time/speed

- -

Of course you can also do something like:

- -
    WAIT=5 WALLS=1 CANNIBAL=1 ./prog
-
- -

Which although might not seem like it is significantly slower.

- -

Moving the snake about to create patterns

- -

If you press different direction keys in some pattern you will see a pattern -form too (entirely dependent on how fast you react, what keys you press and I -suppose also how much the bug moves and the chance of being eaten also).

- -

Other ideas

- -

Here’s an idea: how many moves is needed to make it possible to have some sort -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
-
- -

On my 13" MacBook Pro at the default settings going horizontally the snake ran -into itself at:

- -
    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!

- -

Gameplay mode: Grow-Shrink mode (Positive and Negative Winning)

- -

In this mode you’re simultaneously growing and shrinking but because the max -size is unsigned you can win both by shrinking to the point of losing your body -or growing to the full size directly. This is how it works:

- -

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
-
- -

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 -don’t get the bug) you shrink by 5. You will find that at times you might only -decrease in size but then at other times - depending on size of screen and how -lucky you are with where the bug is relative to where it last was - you might be -growing. Combining this with the drawing mode could be interesting but I never -actually tried it.

- -

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.

- -

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
-
- -

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 -are called Grow / Shrink).

- -

Hypnotic / Sleepy mode (Photosensitive epilepsy/Stimulation overload trigger)

- -

Photosensitive epilepsy/stimulation overload trigger warning: -For those who have epilepsy or are susceptible to stimulation overload from -flashing lights and patterns please do be careful with this mode. Depending on your -susceptibility this could very well trigger seizures or other discomforts and I -wouldn’t want that to happen to anyone. The default wait time is 231ms, the -typical range of seizures is 3 to 30 flashes per seconds (but can be higher) and -that means that at EVADE=1 every 231ms there will be a flash in addition to the -snake moving too.

- -

Anyway this might sound absurd but it can be somewhat hypnotic by doing:

- -
    EVADE=1 CANNIBAL=1 ./prog
-
- -

And just moving the snake about not trying to catch any bugs - just let whatever -happens happen.

- -

Gameplay mode: Stimulation / Calming mode

- -

If you just need something to stimulate your mind to keep you calm or simply you -want to play a game without having to worry about winning or losing you can set -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
-
- -

Gameplay mode: Expansibility

- -

As I was writing this document and the game modes I have thought of other game -modes (they were added) and I have come back to this document to add more modes. -That’s the beauty of the variables I have implemented: together with the -imagination and even just chance different modes of play can be devised. The -drawing mode was a happy chance of how the timeout() function works for -example.

- -

As a person who was heavily involved in MUD development and design I know very -well how games need to be fun and have different options, not be too easy but -not be too hard either. And a game developer who does not play their games is a -game developer who is detached from their player base and the wants and needs -are therefore disregarded if even heard. I have played this game a lot for this -reason as well as I simply enjoy it and thinking up new modes.

- -

5. Scripts

- -

The following scripts are provided to simplify and automate some of the things -this file discusses as well as allowing to easily change the colours.

- -

Colours selection: snake-colours

- -

To do this use the snake-colours script like so:

- -
    ./snake-colours
-
- -

If you want to pass in specific variables you can do that too e.g.:

- -
    CANNIBAL=1 ./snake-colours
-
- -

The script will prompt you for the colour of the walls, snake head, body and the -bug.

- -

Automate the above gameplay modes

- -

The play.sh script has the gameplay modes described in this file and is menu -based (there might be differences and some of the modes I include a variation). -The script tests the terminal (allowing you to continue even if problems are -detected), prompts you if you want to select colours (like those in -snake-colours above) and then gives you a list of game modes to play. You can -pass in the game variables but note that if you pass in a variable that the mode -you choose uses then the script will override what you pass in.

- -

To use you can do something like:

- -
    ./play.sh
-
- -

Or to make it so walls are an obstacle:

- -
    WALLS=0 ./play.sh
-
- -

6. Notes about some of the game variables

- -

The game variables can be used for a variety of things including different -gameplay modes (as shown above) but here are some notes about a few of them.

- -

WAIT: Snake speed

- -

Besides the different game modes, if you find it too fast/slow you can change it -to a value that’s better for you. Remember it’s a wait time so if you want the -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 -occupy. It might be very close to it and I do not ensure it’s a new place at -all.

- -

Each time the bug is placed the counter will be reset whether from eating the -bug or not reaching it in time. Default 300 moves.

- -

This can help you get out of a trap but it can also sometimes make it harder.

- -

SIZE / MAXSIZE: Initial / Max snake sizes

- -

997 might be a bit high for many people. Or maybe you don’t have enough time. Or -you’re not in the mood to lose. Or you want to get close to a size you were last -time but quickly. It might be in conjunction with drawing mode.

- -

Note on snake size: there’s a stated size (in the score line) which is what -the snake will ‘populate’ in the snake arrays and the visible size: as I have -discussed here and there the snake doesn’t grow immediately so it takes time -before the snake is visibly that many characters. In other words just because it -says SIZE 29 doesn’t mean it will instantly be 29 characters on the screen.

- -

GROW / SHEDS : Growth / Shedding sizes

- -

Similar to the initial size. Notice that the above point on size applies here: -it will take the number of movements before it is ‘its full size’.

- -

7. Conclusion (and inspirational words for everyone)

- -

I believe that this file has given more than enough information for anyone to -get an idea of what is possible with my implementation of Snake. I realise also -that most people won’t have read this far and that’s quite all right; I’m very -thorough and pay attention to details obsessively. Creating this game was a -delight and I am quite proud of what I have done.

- -

Thank you for whatever level of attention you felt you could or wanted to give -and enjoy it in whatever way you see fit!

- -

Much more importantly is that Snake aside I hope I have demonstrated how with -the imagination and creativity almost anything is possible. This is something -that applies in our world in every way and everyone can do something amazing if -they believe in themselves! I would have never thought this the case for me -years ago but I know it’s true now. Believe in yourself. Always.

-
-
- - - - -
Creative Commons License

© Copyright 1984-2020, -Leo Broukhis, Simon Cooper, Landon Curt Noll -- All rights reserved
-This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

 
- - diff --git a/2020/ferguson1/ioccc-and-chocolate.pdf b/2020/ferguson1/ioccc-and-chocolate.pdf new file mode 100644 index 0000000000000000000000000000000000000000..2ca469d15e7400c9fd8f1205ca6fcf07441c946f GIT binary patch literal 46195 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 E2OLG|!vFvP literal 0 HcmV?d00001 diff --git a/2020/ferguson1/play.sh b/2020/ferguson1/play.sh index 56914d47d..ac153dc15 100755 --- a/2020/ferguson1/play.sh +++ b/2020/ferguson1/play.sh @@ -229,7 +229,7 @@ function play() while true; do menu - read -rp "Make your selection (0 - 7 or any other key to exit): " mode + read -rp "Make your selection (0 - 16 or any other key to exit): " mode case "${mode}" in 0) # Drawing mode 0 diff --git a/2020/ferguson1/spoilers.html b/2020/ferguson1/spoilers.html deleted file mode 100644 index 8424e3e84..000000000 --- a/2020/ferguson1/spoilers.html +++ /dev/null @@ -1,970 +0,0 @@ - - - - - - - - -
- IOCCC -

The International Obfuscated C Code Contest

- -

Supplemental Info

-
- - -

Introduction (SPOILER ALERT)

- -

In the gameplay.md (gameplay.html) file I have given enough -information to know how the game looks, how to move the snake, how to pause etc. -In this file I am including (some) information on how the game actually works: -the technical details as it were, obfuscation techniques and also some of the -ways I reduced the iocccsize so I could do more with the code. Probably this -file causes more spoilage than the other files though that’s not to say -everything will be answered here.

- -

If you’re still interested in this file (i.e. you’re a judge or someone who -wants to know more about it) read on. Yes I write a lot. But you probably -already knew that so no harm there, hey?

- -

- - - -

How does it work?

- -

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 -each the 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 -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.

- -

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 -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 -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 -is >= the max size the game is won (the winning size will be shown as the max).

- -

Else the arrays are updated; when the array is being updated the body of the -snake is drawn. After this the head is drawn. Depending on the length of the -snake wrt the size the snake technically it might be that the number of -characters the snake takes is increased rather than having moved the snake -forward (i.e. the tail moved by one). This registers where the snake is and -where it was but no longer is (important particularly for negative growth/shed -sizes).

- -

After that a bug is placed (but not drawn) in a pseudo-randomly selected area of -the field. This function takes a parameter which if not 0 and the bug had -already been placed somewhere (in other words if the bug was just eaten) call -the snake function. As long as the game isn’t won at that point find a new place -to place the bug.

- -

Next the score line is updated and then whilst the game is not over (player -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 -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 -running into walls regardless of that feature status - this simplifies things -greatly (see below for more details).

- -

If cannibal mode is disabled (the default) and the snake runs into itself then -it’s also game over. Or did it run into the wall? If walls are passable (the -default) it will come out from the other side; else it’s game over. If it comes -out on the other side and the snake is there and cannibal mode is disabled then -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 -example shedding and shedding size.

- -

Finally if the snake’s new coordinates are on a bug a new bug is placed on the -field, the snake grows and the score is updated. Otherwise if the bug has waited -the number of moves that it waits before it finds a new place the bug is placed -in another location too (and the snake does not grow). In either event the snake -location is updated and redrawn.

- -

There’s also the processing of any shedding at this point. If the snake eats a -bug the snake will not shed; if it sheds the snake will not move in the same -manner as if it simply moved. Growth/movement can in some ways be conflated.

- -

How does the snake movement work?

- -

The way the snake looks to move is that every time the snake moves the last -position (the tail) is replaced with a space (' '). If the snake eats a bug -then the snake will get bigger in the next N movements. Note that if the snake -isn’t its full size it will only be growing and so might not appear to be -moving at the tail end; I have discussed this more than enough elsewhere.

- -

There are two arrays that store the last Y/X coordinates of the snake’s -positions that are shifted forward as the snake moves about the field looking -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 -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 -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).

- -

Collision detection

- -

This was an interesting thing to work out. Because the snake could occupy more -than one row and more than one column at a time the two arrays approach seemed -good to me.

- -

I already mentioned how the array is shifted but to be clear: the snake head is -always at element size - 1 and the tail is at 0 and so it’s just a matter of -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! :)

- -

Cannibal collision detection

- -

How does cannibalism change collision detection? The fact that each time the -snake function is called there is a space printed at the tail end was a problem -to resolve: what happened is if the snake went through itself then at some point -there would be a space that breaks the snake into more than one piece (where the -tail was but that had been another part of the snake)!

- -

So what I do: first the tail is cleared, then as the snake coordinates arrays is -being updated I redraw the snake chars. Then at the end the head is drawn (it -used to be the head was drawn first but by drawing it after you can see where -the head is inside itself, creepy as it is). Thinking on it this very possibly -is why (or partly) the negative shedding leaves the head somewhere in the middle -of the snake (or that the head of the snake is left behind) but again I don’t -think that as a problem.

- -

There are other strange ways this looks. Not only does it look like lines -intersecting at any number of places but it looks particularly strange when the -snake goes the opposite direction: it has to unwind as it were, and for a moment -you might not see what is truly happening. There are two ways you can look at -this I think.

- -

First is that it’s physically impossible in real life so the fact this cheat -mode exists for a different kind of play means it should look unnatural: for it -is unnatural!

- -

The second is you can look at it as if two more snakes are having fun with each -other. This can also appear to defy physics!

- -

Choose your poison. Or rather your venom.

- -

Collision detection when resizing the window of the game

- -

What happens if the window is resized during the game? If the size is increased -then the border can (and will) stay the same; but if the window is shrunk then -what? It would be very difficult if not impossible to redraw the snake: where -would the snake be and what about the border? The arrays would have to be -adjusted too! Another issue: what if the snake is beyond the new walls?

- -

The best thing I could think of is that if the window is shrunk then the snake -is assumed to run into the wall: in other words the game is at that point over. -If the window is increased in size nothing happens other than the game won’t be -the full window size (or technically a few rows/columns fewer): everything will -remain the same. This GREATLY simplified things.

- -

There was another problem at some point where the snake would quickly grow if it -was increased in size but I believe that’s resolved. Anyway what kind of field -in real life increases in size like that so suddenly? :)

- -

Obfuscation

- -
    -
  • I make use of the C token pasting operator. Although this is well known it’s -probably less known and either way it’s not as simple as just having the code -that it translates to. For example why have:

    - -
          attroff(COLOR_PAIR(L));
    -
    - -

    When you can have instead:

    - -
          attroff(m(PAIR(L)));
    -
    - -

    It’s an extra layer of indirection. This used to be more used but when - making it easier for users to change the colour pairs I no longer used it to - set up colour pairs. It might not defeat cpp but it does make for more - reading otherwise (depending on how you look at it maybe less?).

    - -

    It also helps save iocccsize bytes (but not as many as it used to!).

  • -
  • What is this block of code?

    - -
          F(MAXSIZE)g d g = f((a)((*y-2)*(y[-2]-2)),g);
    -      F(GROW)N d
    -      F(SIZE)A d
    -      F(WAIT)y[-3] = (int) strtol(z,NULL,0));
    -      F(EVADE)W d
    -      /* and so on */
    -
    - -

    There are several things going on here but I’ll focus on the main one here and - leave the others to their own: the F macro and the associated d macro are - here important.

    - -

    In addition to saving many bytes this also makes for interesting reading. - What this actually is doing is checking for environmental variables. If - getenv() does not return NULL then strtoul() is called on it (except for - the one that clearly shows strtol()).

    - -

    There are other things going on (below).

    - -

    Now it might not defeat the cpp but can you tell me what this is even with the - cpp?

    - -
          f((a)((*y-2)*(y[-2]-2)),g);
    -
    - -

    The cpp might get it closer to what you might expect in a normal program but I - refer to the array offsets (below also).

  • -
  • Stringification: there isn’t a single " in the source code! The F macro -uses the macro Z which does the real work. At first F was only getenv but -I changed it to use stringification. Following the creation of the Z macro I -made it use it instead. If I remember rightly that uses extra bytes but somehow -it felt better this way in my head. Then to save many bytes I made the macro do -much more than that (that is the F macro).

    - -

    As for the Z() macro it means that I could remove "s; for example:

    - -
          #define J Z(X:%d/%d Y:%d/%d S:%zu/%zu B:%zu\n), Y, y[-2], X,*y,A,g, c);
    -
    - -

    Which as I have discussed elsewhere is the entire set of parameters to both -mvprintw() and printf() plus the end of the function call (i.e. );.

    - -

    BTW: There’s another really cool benefit of stringification: because more than -one space is converted to a single space I can have it spread across columns on -the format and even new lines if necessary. This fact along with the next -technique causes some beautifiers problems too.

  • -
  • Another use of the Z macro is this curious line:

    - -
          static char s[]=Z(; +<>^v);
    -
    - -

    There are a number of curious parts. First is the Z() part makes it look - like a function call. Of course that wouldn’t work as such because an array - initialiser needs an initialiser list or a string literal. Whether that’s - immediately obvious to others I don’t know but then how is this even possible? - A ; followed by , +, <, >, ^, v?

    - -

    A very minor factor is that the character ‘v’ is there and it’s (v) also - used as a macro (well, define) ‘might’ be of something to some people. Or - programs maybe? For instance using in vim * on the v in that array and - it takes you other instances of v: as in \<v\>: which happens to be - something else entirely. That itself however might or might not be - significant for this instance!

    - -

    But what function would take the string (…function and string?) that it takes? - The sequence of characters would normally cause compiler errors like that. - I have it in the function that uses it; static since it need never change: - only one copy is needed. Why I have it in the function is to move it further - away from the definition of Z(). I will also be returning to this one - again.

    - -

    Another example is using the E() function e.g.:

    - -
          E(Z(YOU WIN!));
    -
    - -

    Which happens to be in the same function the above char array is defined. Also -it’s more like a string than anything but yet it’s not visibly a string.

    - -

    Some beautifiers don’t like this.

  • -
  • There’s an #undef following an if in a way to draw attention from what to -do if the if is true.

  • -
  • There are a couple case statements that fall through to the next case -condition. Actually one falls twice: into the one below which falls into the one -below that. This means the order it is in is required. It also reduces the -iocccsize a reasonable amount (by having it in the main loop I could do these -things which reduced the number of keywords needed).

    - -

    When quit is requested it falls into pause. But then pause falls into the - default (so no recognised key is pressed) which does x = 0;. Outside the - switch block there’s if (x) P = 0;. So what this amounts to is: if quit - is requested pause will be changed to the opposite state. This will then - fall to setting x to 0. And because x is 0 the state of pause won’t be - updated again. Now because Q = 1; (or actually l) it doesn’t matter if - P || !P because Q will also jump to the top of the ‘loop’ only to exit - since the condition no longer is true.

    - -

    But then if pause is pressed the same thing applies: it will be updated to - the opposite state and then it will fall to the default case which sets x = - 0; which means it won’t update pause outside the switch block. If pause was - enabled then the loop will start over only this time because quit wasn’t - requested it will re-enter the loop.

    - -

    So basically quit can both pause and resume but it doesn’t matter because it - will still quit; and pause will fall to the default statement without being - updated again either. In other words the only time pause will be updated - (disabled) outside the switch is if a direction key is pressed. But of - course it’s disabled inside the switch if pause key is pressed when it’s - already paused! This removed three break statements because the order allows - it; if the order was changed then the code would break in another way and - not the good kind!

  • -
  • On the note of pausing there’s at least one instance of removing if..else -(besides those in e.g. environmental variable processing) that’s curious: I -converted this:

    - -
          if (!P) P = 1;
    -      else P = 0;
    -
    - -

    To this:

    - -
          (!P && (P = 1))||(P=0);
    -
  • -
  • 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()).

  • -
  • And on that note: makes use of the ugly goto in numerous places. This -includes simulating a while() loop, breaking out of that ‘loop’, breaking out of -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 -it might be easy to miss.

  • -
  • Uses an unnamed array (compound literal) for several different game variables. -The pointer to the first element is defined like int *j:

    - -
          static int Y,X,*j = (int[8]){231,0}, *U, *V, *e, *n, Q, P,x,*y,l=4,*D;
    -
    - -

    Does the 231 seem familiar? It should: it’s the default key delay for the - timeout() call (which I will return to again). The rest are 0 default. But - what about j? Is it even used? Actually in main:

    - -
          y = j+3;
    -
    - -

    But aside from that j is not used. However…

  • -
  • Major abuse of negative pointer offsets along with confusing names. I’ll say -this in two parts: the negative offsets and then the confusing names.

    - -

    Can you honestly tell me that this is easy to follow?

    - -
          if (Y < 1 || Y >= y[-2] || X < 2 || X >= *y || y[-2] >= y[-1] || *y >= 1[y])
    -      {
    -          if (*p && !(*y>=1[y]||y[-2]>=y[-1]))
    -          {
    -          if (Y<1||Y>=y[-2])
    -              Y = Y<1?y[-2]-1:1;
    -          if (X<2||X>=*y)
    -              X = X<2?*y-1:2;
    -          }
    -          /* ... */
    -      }
    -
    - -

    That’s not the only place negative offsets occur! Observe the ternary operators - there too.

    - -

    I’ll go back to the F() macro and in particular what follows one of them:

    - -
          g d g = f((a)((*y-2)*(y[-2]-2)),g);
    -
    - -

    To parse that at the very least you have to know that y points to j + 3. - Then you have to know that j points to the first element of an unnamed - array (compound literal). But then you also have to know that that array has - different types of data. Once you figure out the elements in question you - then have to account for the offset that y is. Then once you have that - figured out you need to consider the negative offsets to y (this also - applies to the block above of course and maybe more so). Then you have to - think of operator precedence etc. I mean what is:

    - -
          (*y-2) * (y[-2]-2])
    -
    - -

    …about? An offset of -2 and subtract -2 from that. Multiply that by the *y - (which is j[3]) - 2.

    - -

    But why is there a g d g = when the other lines only have the variable (or - location) and a d? (Excepting the one that doesn’t refer to d.)

  • -
  • On the confusing names etc.: the getmaxyx() function gets the maximum Y, X -dimensions. Now in the first call in main() I have the code and tests following:

    - -
          getmaxyx(v, *y, y[-2]);
    -      --*y; --y[-2];
    -      if (y[-2] < 9 || *y < 9)
    -          return E(Z(term too small));
    -
    - -

    Clearly the max x must be stored in *y and the max y must be stored in .. - y[-2]. After all anyone knows that the coordinates are in the order (X,Y)! - In fact the output of my entry and my documentation demonstrates this - clearly: X before Y! It’s so true in fact that the documentation for curses - more than once states that it’s actually (Y,X)! But the fact I don’t even - refer to a x is possibly confusing even without the negative offset.

    - -

    Of course I don’t have to use negative offsets at all! I could use positive - 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. - 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 - 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!

  • -
  • Misleading variable names for coordinates. In other words the Y coordinate is -called X and the X coordinate is called Y. This adds to the already confusing -array references but it also adds to the confusion of the order of the curses -functions coordinates: it appears that yes X does come first because the -function calls that take coordinates suggest it - but in actuality it’s just -that the names are deliberately swapped. Of course that’s after you determine -that the variables X and Y are only the snake head but at that point you -still have to figure out the array confusions. Also if you’re tired V could -easily be read as Y and I have a lot of fun with V which I will get to next -(and again). Finally why not have them be two different names unrelated to -coordinates? I thought of this and I think it’s more confusing to have something -that is associated to what it is but incorrectly rather than something unrelated -entirely: the mind constructs things from what it has seen, what it experiences, -what it knows, and since X and Y are used for coordinates the natural response -of the brain is to assume Y is what is really X and X is what is really Y.

  • -
  • More confusion with variable names: If you notice the int *U, *V they are -also part of the compound literal: that is they point into it:

    - -
          U=y+2; V=U+1;
    -
    - -

    Except that these two are done in two different places: away from the - assignment of y to remove them further away from the contexts they are - used in.

    - -

    There’s another reason for moving it further down: if you look at the bug - drawing function (which comes before main()) you will see that there I set - the bug location to refer to the y ‘array’. I do blank out the old - location at *U, *V but then assign the new location differently. Then in - main() I draw it at *U, *V and also check for the bug being eaten there - too.

  • -
  • There’s another array that holds a variety of data.

    - -
          static k p[5]={1,0},W=300;
    -
    - -

    It holds the following information (but not necessarily in this order):

    - -
          WALLS
    -      CANNIBAL
    -      SHED
    -      SHEDS
    -      EVADE
    -
    - -

    But how will you know that just looking at the declaration? Many variables - and they’re part of collections but the many layers of indirection cause - confusion to the already unhelpful variable names (for those that even have - names!).

  • -
  • There’s an array of pointers to functions too: it holds three functions but -rather than always use the array I only do some of the times. I even use the -same function in the same general area with both names in order to mislead. -Incidentally splint seems to have a parse error on the declaration though I -don’t know why.

  • -
  • On arrays/pointers in general I also use e.g. 3[p] and p[3] (there are -many other examples that I have already shown).

  • -
  • Expanding on referring to the same thing in more than one way take a look at:

    - -
          case KEY_LEFT:
    -          V[1] = l-3;
    -          break;
    -      case KEY_RIGHT:
    -          V[l/l] = l/2;
    -          break;
    -      case KEY_UP:
    -          V[l-3] = l-1;
    -          break;
    -      case KEY_DOWN:
    -          V[l/4] = l;
    -          break;
    -
    - -

    What’s going on there? First of all the letter ell looks very much like the - digit one but above there are only two references to the digit 1; the - others are the letter l. If you’re tired though, don’t have the best - vision (I’m speaking from experience here and that’s not even considering - the font) it would be easy to miss. Online in a text editor you have syntax - highlighting of course but there’s more to it than that. Also in other parts - of the code there are references to the digit 1 instead of the letter ell as - part of array indexes too (so which is it?).

    - -

    Second is that the index referenced in each of the case statements evaluate - to the same: 1. That’s all accomplished by taking advantage of the value of - l: 4.

    - -

    And then to further make use I have done the same for the value to assign to - that element: 4 - 3 is 1, 4 / 2 is 2, 4 - 1 is 3 and 4 is obviously 4.

    - -

    But doing it this way means you have to first of all know that there’s the - variable l (conveniently declared at the top of the file); then you have - to determine (once you’re at this place in the code) if it’s the digit or - the letter - for each reference (since both exist)! After all it’s entirely - possible that I could have negative offsets here (and there’s nothing that - would prevent negating a variable either in this case): I have done it many - other places! But even after all of that you still need to do the - calculations when I could simply have had:

    - -
          V[1] = 1; V[1] = 2; ...
    -
    - -

    Etc.

  • -
  • Variable reuse. For example in the function E() there’s a variable that keeps -track of whether or not it’s been called; that variable is h and it says -whether to call endwin() or not in as in some cases E() is called more than -once. Now instead of setting the variable h to 1 I set it to l: it looks -like a 1 yes but it’s actually 4. Does it matter that it’s 4? No. The only -thing that matters is that it’s a non-zero value. The same applies to Q which -I also set to l.

  • -
  • I also use *y as a boolean. If you observe that I set x to *y (which -itself might make one believe they’re related in some form): it used to be just -a 1. However it is only used as a boolean. *y as I described as the LINES -variable on invocation of the program. Since I check if it’s < 10 before -continuing the game I know that it will always be non-zero. But what if someone -tries LINES=0? In fact it will be ignored: if the terminal is 25 lines and you -specify 0 lines then it’ll be 25. Therefore I can use it as a boolean here since -it needs there to be non-zero. Then in order to get the opposite, 0, I can do -!*y which I do elsewhere.

  • -
  • More generally on variable reuse: the compound literals and arrays of the same -data type but which are completely different things means I can use the same -name in different parts and so it’s then a matter of determining which -element/offset to use.

  • -
  • Why do I compare if the original dimensions are >= the current when the idea -is to make sure that they’re actually bigger? How does that work? Why not just -check if it’s > than the current?

  • -
  • More confusion with the max x/y dimensions: Going back to max x/y dimensions -and coordinates of the snake; I showed this code before:

    - -
          if (Y < 1 || Y >= y[-2] || X < 2 || X >= *y || y[-2] >= y[-1] || *y >= 1[y])
    -      {
    -          if (*p && !(*y>=1[y]||y[-2]>=y[-1]))
    -          {
    -          if (Y<1||Y>=y[-2])
    -              Y = Y<1?y[-2]-1:1;
    -          if (X<2||X>=*y)
    -              X = X<2?*y-1:2;
    -          }
    -          else
    -          {
    -          /* ... */
    -          }
    -      }
    -
    - -

    I asked if you could honestly figure that out easily. But here’s a bit more - elaboration. I already talked about the negative offsets - and they should - talk enough themselves - but look at the mess. Even if they weren’t negative - offsets there are a lot of array references (though by a pointer to an - element in the unnamed array) and comparisons all jumbled together. There’s - more.

    - -

    I have the ! of a || to flip the logic: that’s not how a normal person - would write code. Well…not usually, I guess; I have done it in times where - it seems more appropriate but it’s not the way it’s usually done (I think?).

    - -

    Even so that part is doing more than one thing: Not only is that code making - sure the max dimensions aren’t smaller (from the original) but it also makes - sure that the wall isn’t hit (when it’s not allowed) as well as making the - snake go through to the other side of the wall if the snake is allowed to - and hits the wall! And even when the snake is allowed to go through walls if - the screen size has shrunk it will reach the else part.

    - -

    It also has the ternary operator to adjust the coordinates of the snake head - should the snake go through a wall and those also have negative offsets. But - how can you figure that out just looking at that mess? Unhelpfully the X/Y - seem to both be compared to y but never x (that variable is unrelated - and not even in this part of the code).

  • -
  • The function B() ends with a goto. Why? It’s also another place where -there’s at least one negative offset.

  • -
  • A note on the many #defines in my entry. You say you dislike them:

    - -
          Doing masses of #defines to obscure the source has become 'old'.  We
    -      tend to 'see thru' masses of #defines due to our pre-processor tests
    -      that we apply.  Simply abusing #defines or -Dfoo=bar won't go as far
    -      as a program that is more well rounded in confusion.
    -
    - -

    As I have noted though many of these save quite a lot of bytes too. It’s - true that they might aid (but barely) in obfuscation but I think that my - entry is more rounded in confusion; certainly the other techniques I have - mentioned include much more than just #defines. One of the #defines that - saves a significant number of bytes is of course J: I use that twice and - it would be a huge burden to include it twice!

    - -

    At the same time I have removed some #defines that have added bytes; I’ve - also removed some that saved bytes and in fact I think it makes it more - obscure if in no other way but making it more difficult to look at (for - example I might have been able to save bytes by defining for the array - offsets but I find that it looks much better - which is to say much worse - - the way I have it now).

  • -
  • Going back to the curious static char s[]=Z(; +<>^v);: there’s something -magical about the number 7 isn’t there? I do not refer to primes although -certainly one could say that primes have a magic to them. No. I’m instead -referring to another property: bitwise AND. For example consider the following:

    - -
          7 & 0
    -      7 & 1
    -      7 & 2
    -      7 & 3
    -      7 & 4
    -      7 & 5
    -      7 & 6
    -      7 & 7
    -
    - -

    What is the result of each operation? Quite simply it’s the number itself! - How does this relate to the s array though? Let me back up. Is there any - place in the code that has the number 7 (by itself - 997 doesn’t count)? A - quick check will answer that: no. But here’s a curious thing:

    - -
          q X, Y, s[*D+2&(1<<3)-!Q]);
    -
    - -

    We KNOW that at this place Q is 0 so the !Q has to be 1. Now 1 << 3 is 8. Therefore

    - -
          (1 << 3) - !Q == 7
    -
    - -

    But what’s *D? If the snake hasn’t moved once yet it’s 0; otherwise it’s the number - of the direction the snake is currently facing. Thus

    - -
          *D&(1<<3)-!Q
    -
    - -

    …refers to *D. However if I were to have that be the element it wouldn’t - work right: I wanted the semicolon in there but the space was needed to - reduce the iocccsize down by one (ironic that adding a byte reduces it by - one but there it is). I could have had it later in the array but this felt - slightly more confusing: more operators (so precedence) to consider (and in - fact GCC complains about this but as is clearly demonstrated it’s not - important). By adding a +2 it adjusts the index to be the correct value in - order to show the proper index of the char array.

    - -

    But how do you know this? You have to know first of all that Q will always - be 0 at this point (so therefore !Q == 1). You also have to understand that - in the range N = 0..7 the value of N & 7 is N (which might not be - something everyone realises). Obviously you need to know that (1 << 3) is 8 - in order to get the final *D & 7.

    - -

    You furthermore need to know that *D will always be in the range 0..4 - which means that *D & 7 will always be in the range 0..4 (If I were to - have added a & 7 to the end it’d still be the correct value! But the use - of bytes couldn’t be justified there.) Finally adding 2 will get the correct - value of the array index. After all that it will print the correct character - for the head!

    - -

    But there’s another trick here: there’s never a direct assignment to *D: - D is set to 1 + V and *D is never touched directly: it’s all through - 1[V]. The references to directions i.e. KEY_LEFT etc. and also the - switch() to see how to modify X/Y are all done through the V pointer. But - then what is V anyway? It’s U + 1; and U happens to be y + 2. But - y happens to be j + 3. Of course j is a pointer to the first element - of the compound literal! So there are quite a lot of levels of indirection - in this part alone (not to mention other places)!

  • -
- - -

Obfuscation in prog.3.c and prog.3-j.c

- -

There are a few differences in this version that are worth noting. First of all -is a define that’s in the compiler invocation Makefile is in the source file -itself. Second are the following obfuscation techniques. To simplify integrating -the explanations I have simply yanked and pasted them below:

- -
    -
  • Variable reuse in ways that might not be so intuitive. For example in the -function E() there’s a variable that keeps track of whether or not it’s been -called; that variable is h and it says whether to call endwin() or not as in -some cases E() is called more than once. Now instead of setting the variable h -to 1 I set it to *y. Now as you can see *y starts as a NULL pointer; this is -set to the compound literal j + 3. This in turn is what LINES is (number of -lines). So y = j + 3; and j I discuss elsewhere. But what if LINES is set to -0? LINES being set to 0 sets it to the terminal max size but in any event I test -for values < 10 and if either LINES or COLUMNS is < 10 then E() is called. -However E() always returns 1 and since this is in main() and it’s an error I -simply return E() and thus it’s only called once. Similarly for Q I set it to -l. But then..what is l?

  • -
  • In fact l is assigned a value 4 via a compound literal. It’s declared at the -top of the code and then in main() assigned like: l = ++(int) { 3 } ; which -equates to 4. I think that’s pretty cool!

  • -
  • What is this? for(I=0; A&&I<A-(A-A||A); ++I) In fact the A-(A-A||A) is -equivalent to A-1: A here is always > 0 so A-A is 0; however because A -is non-zero the expression A-A||A is equivalent to 0 || A and since A is -non-zero we know that the value is 1 and thus it’s A - 1! I quite like this -technique: it doesn’t make sense at first glance and it probably remains elusive -until you look at it more thoroughly. It compares that I < A - 1 but the 1 -is calculated by subtracting from A the value of subtracting A from itself -|| A and A is always non-zero (as the condition says) which means it’s -A - 1!

  • -
- - -

There might be more things that differ but those are the significant ones.

- -

Of course none of the above is so easy to see without a beautifier. Why did I -choose the format I did? Well it came about in a sequence of possible layouts. I -tried first to make it look like the game might itself but the trouble with that -is it’s too random. Then I thought maybe I could spell out the words SNAKE 2020 -or IOCCC 2020 or something like that. But these were proving to be a problem for -me; what I was seeing on the screen just wasn’t looking right in my head.

- -

At that point I just started playing with different patterns and I quite like -what it’s turned out to be. I don’t know what I would call it but one could look -at it and think of columns holding up a structure. There’s also a symmetry to it -and almost certainly that’s what is most attractive about it to me for I love -symmetry.

- -

Some final notes on the layout I’d like to point out. First there are 53 lines -counting blank lines. Second is that if you count the number of lines (of code) -in each ‘block’ they should all be a prime (except the two blocks of 1, I -guess). Third is that I tried to make sure that each column of the code (not -counting the preprocessor - see below on that) start and stop at the same -position (as in column in the row/column kind) on each row. Below I show the -start and end column of each horizontal block. The #undef in the middle should -be centred based on its length and the 120 columns for total. The preprocessor -directives should also be centred in that way (that was my goal anyway).

- -

The following were intentional:

- -
    -
  • 53 lines.

  • -
  • The number of lines per vertical block (3, 5, 3, 11, 7, 1, 1, 5, 7).

  • -
  • Number of horizontal blocks (5)

  • -
  • Number of vertical blocks (9)

  • -
  • iocccsize 2053.

  • -
- - -

The symmetry was even more important to my mind; observe that each vertical -block (or column) starts and ends on the same column (or at least that was my -goal; I think I did it but maybe I missed one here and/or there):

- -
    -
  • Vertical column #1: 5, 22
  • -
  • Vertical column #2: 25, 41
  • -
  • Vertical column #3: 49, 64
  • -
  • Vertical column #4: 73, 90
  • -
  • Vertical column #5: 97, 120
  • -
- - -

Furthermore based on the length of each C preprocessor directive (the -#defines, the #includes, the #undef) I have attempted to centre them in -the file based on the 120 columns total. I believe I have succeeded in that -though again maybe I have an off by one or two here and/or there.

- -

Most of the primes above were intentional.

- -

Skinning the snake (i.e. decreasing the iocccsize)

- -

This happened in a number of ways and I have already documented some in other -parts. Now there are a number of reasons to do this: amongst them to make it -possible to obfuscate (plus a more unique layout) but also to add more features.

- -

Interestingly some of the obfuscation saved bytes; others of course added bytes. -For example token pasting saved a few bytes; on the other hand the combining -ifs to the complicated one above added 9 bytes: but another one saved a byte. -I have done this more than once so it’s entirely possible there are others I am -forgetting.

- -

Obviously a more significant one is #define q mvprintw: this saved a total of -47 bytes! This was however done towards the end (or so I thought it was the end; -it was anything but) when I started looking at obfuscation rather than adding -features. But then because of this I looked at adding more features!

- -

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 -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 -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 -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)
-
- -

However after the loop I had this:

- -
    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 -for A > 1 but I could refer to just I instead of A - 1!

- -

There were quite a few other things besides these and these gains were useful to -add additional features and also to add proper error reporting (when calloc() -failed I called abort() and before that if initialising ncurses failed I also -called abort() and did not report any errors in either event but I do now).

- -

A few more interesting size optimisations

- -

As I was getting to the end of obfuscation (wrt size available - and it was -getting pretty obfuscated too - the majority of the techniques were already in -place at this point) I was looking at the code to see if I could figure out how -to save even more bytes. Here are some that I found interesting ways to go about -the problem.

- -

First, the function that turns on colours looked like:

- -
    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)));
-
- -

But I then saved an extra byte by making it:

- -
    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 -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); }
-
- -

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 -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); }
-
- -

Then this

- -
    !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 -snake’s head will be there!

- -

But why did I check for the bug having coordinates? Because I thought that if -the bug has coordinates obviously the function has been called before which -means that we do clear (write a space) over a place the bug was actually at. But -I thought could there maybe be another way of verifying if the game has started -yet?

- -

Before I ask that question I’ll point out this: is there a reason it has to be -checked? Yes because right after this line new coordinates are found. But -unfortunately the snake has to be placed first since the bug cannot be placed -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,' ');
-
- -

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 -(so game started). What calls to this function happen before the game starts -though? The original placement of the bug. Well if the above check makes sure -that the bug isn’t where the snake is at it means that the snake has already -been placed, right? Going further the snake always starts out at the centre (or -rather the close as possible - rounding maybe an issue depending on the max -y/x): in other words not at 0, 0. This means that the only thing I should have -to check for is that !o: because after this call before the game even starts -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,' ');
-
- -

Technically it would be a " " (see HACKING 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 -display something, it squeezes a few more bytes and having a space is hardly an -imposition on usage (and for that matter having a ‘o’ at 0,0 isn’t either).

-
-
- - - - -
Creative Commons License

© Copyright 1984-2020, -Leo Broukhis, Simon Cooper, Landon Curt Noll -- All rights reserved
-This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

 
- - diff --git a/2020/ferguson1/spoilers.md b/2020/ferguson1/spoilers.md index db4a58625..f392b8981 100644 --- a/2020/ferguson1/spoilers.md +++ b/2020/ferguson1/spoilers.md @@ -274,7 +274,7 @@ technique causes some beautifiers problems too. - Another use of the `Z` macro is this curious line: - static char s[]=Z(; +<>^v); + static const char*s =Z( ; +<>^v); There are a number of curious parts. First is the Z() part makes it look like a function call. Of course that wouldn't work as such because an array @@ -285,7 +285,7 @@ technique causes some beautifiers problems too. A very minor factor is that the character 'v' is there and it's (`v`) also used as a macro (well, define) 'might' be of something to some people. Or programs maybe? For instance using in vim `*` on the `v` in that array and - it takes you other instances of `v`: as in `\`: which happens to be + it takes you to other instances of `v`: as in `\`: which happens to be something else entirely. That itself however might or might not be significant for this instance! @@ -301,9 +301,9 @@ technique causes some beautifiers problems too. E(Z(YOU WIN!)); Which happens to be in the same function the above char array is defined. Also -it's more like a string than anything but yet it's not visibly a string. + it's more like a string than anything but yet it's not visibly a string. - Some beautifiers don't like this. + Some beautifiers didn't like this at all. - There's an `#undef` following an `if` in a way to draw attention from what to do if the if is true. @@ -353,14 +353,14 @@ converted this: - 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()). +same name label for `goto` (there used to be two pairs in `main()`). - And on that note: makes use of the ugly `goto` in numerous places. This -includes simulating a while() loop, breaking out of that 'loop', breaking out of -two loops (a for() in the 'while()'), re-entering (as in starting over) a for() -loop and for dealing with errors. +includes simulating a `while()` loop, breaking out of that 'loop', breaking out of +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` +- 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 it might be easy to miss. @@ -397,7 +397,7 @@ this in two parts: the negative offsets and then the confusing names. That's not the only place negative offsets occur! Observe the ternary operators there too. - I'll go back to the F() macro and in particular what follows one of them: + I'll go back to the `F()` macro and in particular what follows one of them: g d g = f((a)((*y-2)*(y[-2]-2)),g); @@ -412,30 +412,29 @@ this in two parts: the negative offsets and then the confusing names. (*y-2) * (y[-2]-2]) - ...about? An offset of -2 and subtract -2 from that. Multiply that by the `*y` - (which is `j[3]`) - 2. + ...about? An offset of `-2` and subtract `-2` from that. Multiply that by the `*y` + (which is `j[3]`) `- 2`. But why is there a `g d g =` when the other lines only have the variable (or - location) and a d? (Excepting the one that doesn't refer to `d`.) + location) and a `d`? (Excepting the one that doesn't refer to `d`.) - On the confusing names etc.: the `getmaxyx()` function gets the maximum Y, X -dimensions. Now in the first call in main() I have the code and tests following: +dimensions. Now in the first call in `main()` I have the code and tests following: getmaxyx(v, *y, y[-2]); --*y; --y[-2]; if (y[-2] < 9 || *y < 9) return E(Z(term too small)); - Clearly the max x must be stored in `*y` and the max y must be stored in .. - `y[-2]`. After all anyone knows that the coordinates are in the order (X,Y)! + Clearly the `max x` must be stored in `*y` and the `max y` must be stored in .. + `y[-2]`. After all anyone knows that the coordinates are in the order `(X,Y)`! In fact the output of my entry and my documentation demonstrates this - clearly: X before Y! It's so true in fact that the documentation for curses - more than once states that it's actually (Y,X)! But the fact I don't even + clearly: `X` before `Y`! It's so true in fact that the documentation for curses + more than once states that it's actually `(Y,X)`! But the fact I don't even refer to a `x` is possibly confusing even without the negative offset. Of course I don't have to use negative offsets at all! I could use positive - offsets for every time I use negative offsets. But where's the fun in that? - :) + 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.html][] ([HACKING.md][] on GitHub) file. Also there might be some @@ -446,10 +445,10 @@ dimensions. Now in the first call in main() I have the code and tests following: itself! Instead of misleading comments it's misleading statements in the file that explains what some of the things are for! -- Misleading variable names for coordinates. In other words the Y coordinate is -called X and the X coordinate is called Y. This adds to the already confusing +- Misleading variable names for coordinates. In other words the `Y` coordinate is +called `X` and the `X` coordinate is called `Y`. This adds to the already confusing array references but it also adds to the confusion of the order of the curses -functions coordinates: it appears that yes X does come first because the +functions coordinates: it _appears_ that yes `X` does come first because the function calls that take coordinates suggest it - but in actuality it's just that the names are deliberately swapped. Of course that's after you determine that the variables `X` and `Y` are only the snake head but at that point you @@ -459,8 +458,8 @@ easily be read as `Y` and I have a lot of fun with `V` which I will get to next coordinates? I thought of this and I think it's more confusing to have something that is associated to what it is but incorrectly rather than something unrelated entirely: the mind constructs things from what it has seen, what it experiences, -what it knows, and since X and Y are used for coordinates the natural response -of the brain is to assume Y is what is really X and X is what is really Y. +what it knows, and since `X` and `Y` are used for coordinates the natural response +of the brain is to assume `Y` is what is really `X` and `X` is what is really `Y`. - More confusion with variable names: If you notice the `int *U, *V` they are @@ -473,10 +472,10 @@ also part of the compound literal: that is they point into it: used in. There's another reason for moving it further down: if you look at the bug - drawing function (which comes before main()) you will see that there I set + drawing function (which comes before `main()`) you will see that there I set the bug location to refer to the `y` 'array'. I do blank out the old location at `*U, *V` but then assign the new location differently. Then in - main() I draw it at `*U, *V` and also check for the bug being eaten there + `main()` I draw it at `*U, *V` and also check for the bug being eaten there too. diff --git a/2020/ferguson1/terminals.html b/2020/ferguson1/terminals.html deleted file mode 100644 index 759677aca..000000000 --- a/2020/ferguson1/terminals.html +++ /dev/null @@ -1,394 +0,0 @@ - - - - - - - - -
- IOCCC -

The International Obfuscated C Code Contest

- -

Supplemental Info

-
- - -

Terminals

- -

Terminals are very easy to mess up and for that reason they deserve their own -file. I discuss terminal capabilities, dimensions (setting and how to restore -sanity if it causes any problems) as well as colours (limitations etc.).

- -

- - - -

Capabilities support testing program

- -

Although it should be playable without the features I use (except that cursor -movement is required) I have provided a source file that can test your terminal -for the capabilities I use in the entry.

- -

It also tries to determine the minimum number of columns to play without the -status line overflowing - even with the dynamic length. Try:

- -
    $ make test
-
- -

This compiles the program and runs it; you should see something like:

- -
    terminal supports cursor movement
-    terminal supports making cursor invisible
-    terminal supports bold
-    terminal supports colours
-
-    terminal rows  42 (39  playable)
-    terminal cols 157 (155 playable)
-
-    snake size:   997 (max size: 6006)
-      bugs:   199 (max size: 1201)
-
-    at least 34 columns recommended for snake size 997   (is 157)
-    at least 37 columns recommended for snake size 6006  (is 157)
-
-    No problems detected.
-
- -

If I pass in any of the variables GROW, MAXSIZE, SIZE and/or -LINES or COLUMNS it bases its calculations on those variables. For -example:

- -
    $ MAXSIZE=-1 GROW=1  make test
-    terminal supports cursor movement
-    terminal supports making cursor invisible
-    terminal supports bold
-    terminal supports colours
-
-    terminal rows  42 (39  playable)
-    terminal cols 157 (155 playable)
-
-    snake size:  6006 (max size: 6006)
-      bugs:  6001 (max size: 6001)
-
-    at least 37 columns recommended for snake size 6006  (is 157)
-    at least 37 columns recommended for snake size 6006  (is 157)
-
-    No problems detected.
-
- -

As you can see it tells you the max size (the ‘capsized’ snake) as well as -the requested max size (if the requested size is bigger than the capsize then it -will be set to that value instead) of the snake. The capped size is like in the -Snake game itself but the suggested minimum number of columns is based on a -number of variables.

- -

If I were to do the above but set columns to less < 35 then there would be a -problem. But why if the utility recommends at least 37? This is because the -max size of the snake also depends on the terminal size. This utility is not -perfect: I don’t consider shedding; I set growth to 5 if it’s 0 (since it would -be indeterminate how many bugs are possible before ‘winning’ since there would -be no ‘winning’); and there are probably other things too.

- -

As for capabilities: besides cursor movement the others are just nice to have. -From my tests keeping the cursor visible doesn’t ruin gameplay but it is less -distracting if nothing else to have it invisible (in fact due to the sequence of -function calls the cursor would mostly appear to be not in the game field itself - -from memory at least).

- -

Maybe there are other capabilities I should check for that I’m unaware of but -this should give you an idea.

- -

If there are any problems it will say how many (fatal and otherwise). The fatal -problems are if curses fails to initialise or if movement of cursor cannot be -done (both seem odd).

- -

Both scripts run this utility and prompt if there are any problems; -you can continue either way.

- -

Setting the game dimensions

- -

There are a number of ways to do this and only one will keep the terminal sane -all the time but that’s by running it under a terminal emulator that you can -resize the window with first. But what if you want exacts?

- -

The LINES and COLUMNS environmental variables

- -

To specify the lines/columns of curses program per instance use the LINES and -COLUMNS environmental variables.

- -

For demonstration consider the following:

- -
    $ echo $LINES $COLUMNS
-    42 157
-
- -

Note: I subtract 1 from the max y/x and the max coords in the game output -include the walls and score line but these values are for the terminal itself.

- -

Say you want 55 columns:

- -
    $ COLUMNS=55 ./prog
-
- -

This will force curses to detect the max columns (X) of your terminal (for the -game dimensions) - to be 55; it might look like this instead:

- -
    X:27/54 Y:20/41 S:5/997 B:0
-
- -

But problems can arise with messing with dimensions: in my tests this held only -to lines.

- -

Terminal insanity

- -

Terminal sanity can be compromised and it can cause all sorts of output -problems; it might not appear right away but it will show its ugly head -eventually if you use it. For example pager output might be screwy, echo might -be turned off, output of commands might overwrite the input of those commands, -etc. For the game you might not see the final score.

- -

Restoring the terminal

- -

In my tests there is one thing that at least allows you to see the final score -and that is by running clear before running the program. That’s what the -prog.alt version does. You might run it as one of:

- -
    LINES=20 ./prog.alt
-    LINES=20 ./snake.alt
-
- -

Even if the final score is visible though the terminal may very well still be -messed up. There are numerous things you might try but probably the easiest is -to run the game again without modifying the dimensions (and then just quit) or -even just the reset command.

- -

The Linux man page says:

- -
You might have to run the `reset` tool like:
-
-       <LF>reset<LF>
-
-       (the line-feed character is normally control-J) to get the terminal
-       to work, as carriage-return may no longer work in the abnormal state.
-
- -

If you don’t have the reset utility you can try:

- -
    echo -e \\033c
-
- -

Otherwise try clear; stty sane (that will turn on echo also) or if all else -fails log out and back in.

- -

For more information on this subject generally see the Linux keyboard and -console HOWTO (specifically Linux keyboard and console HOWTO section 4). -Yes this works under macOS (at least it did for me).

- -

Terminal size abuse

- -

So players 1, 2 and 3 are content with not meddling with the terminal sizes -outside what’s actually possible with their screen. But then player 4 comes -along and thinks he/she will be clever and mess with the dimensions a bit. Let’s -say they have a 13" monitor and they think it’d be funny to see how 997 lines -would work; they might try something like:

- -
    LINES=997 ./prog
-
- -

What then? Well expect trouble; curses will not know any better and the max -number of lines will be 997. If they were to do:

- -
    COLUMNS=997 ./prog
-
- -

Then they will see a different effect. Again the same reason: the player trying -to be funny.

- -

The game will try and be funny in return and you can expect it to not function -properly. Because that’s what funny means in this context.

- -

Terminal limitations

- -

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 -information on this.

- -

I also limit the terminal size to 10 lines/columns but most likely it would -require the cheat modes to win and in any event allowing certain low values -causes problems e.g. LINES=6 COLUMNS=6 and LINES=3 COLUMNS=3 caused some -nasty issues.

- -

Terminal colours

- -

N.B.: If all you’re after is wanting to change the colours see the script -snake-colours.sh instead. The rest of this file was mostly notes for the -judges; here I discuss the background, foreground as well as limitations (in -particular runtime limitations).

- -

Curses background

- -

The documentation I have seen says that the background will always be black and -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).

- -

Monochrome terminals

- -

According to my tests for monochrome terminals the screen has the white and -black background; if terminal is configured to be black background white -foreground that is the order it is too. You can try that like:

- -
    TERM=linux-m ./prog
-
- -

But I do not specifically check if the terminal has colours. Is there a curses -implementation that has a problem here? Below I explain what to comment out -should you need colours to be disabled but I personally have not run into any -problems even with monochrome terminals. Later I say what to do to remove colour -support in the source code itself.

- -

Colour limitations

- -

According to X/Open Curses, Issue 7 (pg. 57):

- -
With init_pair() and pair_content(), the value of pair must be in a range
-from 0 to and including COLOR_PAIRS-1. (There may be an
-implementation-specific upper limit on the valid value of pair, but any such
-limit is at least 63.) Valid values for f and b are the range from 0 to and
-including COLORS-1.
-
- -

The manpage for init_pair() says this:

- -
       These limits apply to color values and color pairs.  Values outside these limits are not legal, and may result in a runtime error:
-
-       [...]
-
-        COLOR_PAIRS corresponds to the terminal database's max_pairs capability, (see terminfo(5)).
-
- -

Based on the two as well as the fact that monochrome terminals (I’ve tried under -both Linux and macOS) seem to work fine I believe I am fine here too. And to -confirm this I wrote the following code with the output below:

- -
    #include <curses.h>
-
-    int main()
-    {
-    initscr();
-    start_color();
-    printw("COLORS: %d\nCOLOR_PAIRS: %d\n", COLORS, COLOR_PAIRS);
-    getch();
-    endwin();
-    }
-
-    TERM=linux-m ./colour_pairs
-    COLORS: 0
-    COLOR_PAIRS: 0
-
- -

Without specifying the TERM I get:

- -
    COLORS: 256
-    COLOR_PAIRS: 32767
-
- -

In the case that colour is supported the standard states that there will be more -possible colour pairs than I have used so that’s not a problem either. Perhaps -even it would only be a runtime error if the colours are supported and the -number is out of the range? To try and figure this out I did another test:

- -
    int main()
-    {
-    initscr();
-    start_color();
-
-    init_pair(COLOR_PAIRS+1, COLOR_WHITE, COLOR_GREEN);
-    bkgd(COLOR_PAIR(COLOR_PAIRS+1));
-    init_pair(1, COLOR_WHITE, COLOR_BLACK);
-    attron(COLOR_PAIR(1));
-    printw("COLORS: %d\nCOLOR_PAIRS: %d\n", COLORS, COLOR_PAIRS);
-
-    getch();
-    endwin();
-    }
-
- -

Running it like

- -
    TERM=linux-m ./colour_pairs
-
- -

I got the output as if colours weren’t used: 0 and 0. With coloured terminals -however it was not visible. If I however comment out the call to bkgd() I get -instead:

- -
    COLORS: 256
-    COLOR_PAIRS: 32767
-
- -

Which leads me to believe that if you try using a value outside the range it -won’t work for a colour-capable terminal but it doesn’t matter for monochrome -terminals one way or another. And since I don’t go beyond the ranges (as I cited -earlier) there shouldn’t be any problem here.

- -

Disabling colour

- -

Nevertheless if it is a problem and you don’t want to use a monochrome terminal -(or can’t):

- -

First comment out the code that has start_color(); and then add return; to the -top of the C() function (the rest of the code won’t be reached). If necessary -you can also comment out the init_pair() calls.

- -

Perhaps you only need to add the return; to C(). My guess is that’s the case -but I do not know for certain.

-
-
- - - - -
Creative Commons License

© Copyright 1984-2020, -Leo Broukhis, Simon Cooper, Landon Curt Noll -- All rights reserved
-This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

 
- - diff --git a/2020/ferguson1/troubleshooting.html b/2020/ferguson1/troubleshooting.html deleted file mode 100644 index 4de9e88de..000000000 --- a/2020/ferguson1/troubleshooting.html +++ /dev/null @@ -1,474 +0,0 @@ - - - - - - - - -
- IOCCC -

The International Obfuscated C Code Contest

- -

Supplemental Info

-
- - -

Troubleshooting Snake

- -

In this file I have a list of a number of possible problems that could show up -under some situations as well as some things that might appear to be happening -or be a problem but actually are not. For each one I have included a summary, a -more thorough description and any resolutions (from easiest to hardest). For -anything that can cause more than one issue I will list those separately to keep -it as structured as possible/easiest to find.

- -

-- The top wall isn’t (all/some of the time) visible! -- The snake isn’t moving! -- I can’t see the snake head (or other part of the snake)! -- I can’t see the bug! -- The game froze! -- The game gets slower as the snake gets bigger! -- I set SIZE=0 GROW=1, ate a bug but still only the head was visible! Why? -- I like black text on a white background but even with a white background terminal it ends up black! -- The text is hard to see (not bright enough)! -- Can I move the snake faster temporarily without having to modify the wait time? -- On environmental variables more generally and string to int conversions -- Errors and error reporting

- -

The top wall isn’t (all/some of the time) visible!

- -

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 -wide.

- -
    -
  • RESOLUTION #0: Use a bigger screen (it doesn’t take that many columns)

    - -

    Tip: try running make test to get an estimate of the minimum number of columns - needed. This won’t work right if you change the score line!

    - -

    Note that this utility is not perfect; I describe this elsewhere. Either way a - small screen will make it harder to play esp without the cheat modes enabled - (but they can still be fun with large screens).

  • -
  • RESOLUTION #1: Open the prog.c and change the line

    - -
      #define J Z(X:%d/%d Y:%d/%d S:%zu/%zu B:%zu\n),Y,y[-2],X, *y, A, g, c );
    -
    - -

    To be something like:

    - -
      #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 - discrepancies here and there as you’ll see).

  • -
- - -

The snake isn’t moving!

- -

Unless the game is paused this should not be happening. It can appear sometimes -that there isn’t movement and sometimes it might have to take a moment to -‘unwind’ but as long as the head is moving then everything is fine.

- -

The tail might not always be moving which could make it seem like the snake -isn’t moving but as long as the head is moving then the snake can be assumed to -be moving.

- -
    -
  • RESOLUTION #0: Unless the game is paused as long as the snake head is moving -everything is fine even if it seems sometimes that the snake tail is still: this -is normal and there are a few conditions that this might especially show itself -e.g. when the snake is growing.

    - -

    If you want to see this in action try:

    - -
      SIZE=150 SHEDS=10 SHED=1 ./prog
    -
    - -

    Once you start moving you’ll see that the tail stays still for a while and - when it does move you’ll see it doesn’t move for long.

    - -

    Sometimes also it might appear that the movement is delayed but there are - numerous causes for this some of which are a busy computer but ultimately - it’ll catch up; just give it the time it needs.

  • -
  • BOTTOM LINE: If you want more information then see the bugs.md file.

  • -
- - -

I can’t see the snake head (or other part of the snake)!

- -

Possibly this could happen with too long a score line which I already discussed. -It also used to be possible during cannibal mode whilst cannibalising: you would -just have to continue moving on and you’d eventually find your head (assuming -the rest of the screen isn’t covered with your body!). And what else could you -expect if you decided to shove your head up your …. ? :))

- -
    -
  • RESOLUTION #0: Actually I thought it would look better that you could see the -head even when cannibalising so you should be able to lose your head and at the -same time see it!

  • -
  • RESOLUTION #1: If the terminal width is too small then it could be that the -score line is covering the wall, snake and even bug; I gave two resolutions to -that earlier in this file. To get a recommended number of columns use make -test.

  • -
- - -

I can’t see the bug!

- -

This has happened to me a number of times and early in development there were -times that I could swear I even saw it beyond the walls but this should not be a -problem now if it ever was.

- -

However I have made a lot of effort to think of situations that might make the -bug location calculation and placement be a problem and even did some debug -output and wrote another test program to calculate the maximum number of places -the bug could be put etc.

- -

One of the reasons I have the terminal dimensions have a minimum and also a -maximum has to do with the way the bug placement and max size of the snake (and -so arrays) are calculated: it could happen that because of it being unsigned -without there being restrictions it could end up being a bad value that causes -the bug placement loop to never end (in this case the snake would also stop -moving).

- -
    -
  • RESOLUTION #0: If evasion mode is enabled (default after 300 moves) just wait -it out and the bug will move to a new place and it might be easier to see in the -new place.

  • -
  • RESOLUTION #1: The terminal size is too small for the score line. See the -earlier problem for how to resolve this if that’s the case.

  • -
  • RESOLUTION #2: Sometimes you have to look closely for it can be easy to miss. -If it’s against the wall and the screen is Snakey or you’re tired or the colour -contrast isn’t right for you or something like that it might be harder to see. -Try pausing the game (space) and scan the screen for it. If you really are -desperate copy and paste the entire file into a document and search for the bug -char (if it’s an editor like vi/vim you will have to be sure to escape the char -since * is a regexp metacharacter). This has happened to me numerous times.

  • -
  • RESOLUTION #3: If the colour contrast is not good for your eyes use the -snake-colours script to select a different colour scheme.

  • -
- - -

The game froze!

- -

This should not be possible either but without certain safety checks in place I -have caused this (and I did this deliberately in development to find all the -conditions I could to resolve them).

- -
    -
  • RESOLUTION #0: If this does happen it likely is to do with placement of the -bug; that is why I have the max size of the snake capped a fair bit below the -maximum number of coordinates in the game field: because the bug isn’t to be at -a place the snake already occupies.

  • -
  • RESOLUTION #1: If the system rand() implementation is really bad maybe it’s -having a problem finding a location to place the bug. The best I can think of is -to try increasing the screen size or decrease the max size of the snake.

  • -
  • BOTTOM LINE: If the bug cannot find a place to be this will likely cause the -game to freeze because the game loops until it finds an available place for the -bug to be; if the pRNG is so bad wrt where the snake occupies it could -theoretically happen. If this happens you will have to kill the game by e.g. -ctrl-c. Try increasing the screen size and/or decreasing the max size of the -snake. See below for some tests.

    - -

    I removed caps in a test and then:

    - -
      GROW=1 COLUMNS=30 LINES=10 CANNIBAL=1 MAXSIZE=10000 ./prog
    -
    - -

    It froze at this point:

    - -
      X:14/29 Y:5/9 S:323/10000 B:318
    -
    - -

    This is because there was only one place for the bug - where the snake head - was to be. With these parameters the limit is actually 189! This could - perhaps be increased but the calculation I have used is easy and although I - certainly could make it so it’s one less than the max that takes more bytes - and I don’t see it actually mattering.

    - -

    Another time I tried:

    - -
      LINES=6 COLUMNS=6
    -
    - -

    And it also froze (as noted I impose a minimum of >= 10 for both LINES and - COLUMNS).

    - -

    Ultimately I had to kill the program due to the game being stuck in the bug - placement routine. I could have put in a maximum number of loops but what - good would that do? It would use more bytes and what would it do anyway, - win? Well that’s what the max size is for.

  • -
  • TERMINAL SIZE TO MAX SNAKE SIZE CALCULATION ETC.

    - -

    For this information and more use the termcaps utility:

    - -
      make test
    -
    - -

    An example:

    - -
      terminal supports cursor movement
    -  terminal supports making cursor invisible
    -  terminal supports bold
    -  terminal supports colours
    -
    -  terminal rows  42 (39  playable)
    -  terminal cols 157 (155 playable)
    -
    -  snake size:   997 (max size: 6006)
    -        bugs:   199 (max size: 1201)
    -
    -  at least 34 columns recommended for snake size 997   (is 157)
    -  at least 37 columns recommended for snake size 6006  (is 157)
    -
    -  No problems detected.
    -
    - -

    Why are there only 39 playable lines for 42 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.

    - -

    The calculation for max size takes place after decrementing the max y/x; - then it’s (maxy - 2) * (maxx - 2); above that would be:

    - -
      (41 - 2) * (156 - 2) == 6006
    -
    - -

    If for some reason the game were to stall for this purpose you would have to - kill the game by e.g. ctrl-c since the bug placement is stuck in a loop. - This shouldn’t happen though.

  • -
- - -

The game gets slower as the snake gets bigger!

- -

Realistically the game shouldn’t take that much memory but maybe in some very -busy systems it would have a problem.

- -
    -
  • RESOLUTION #0: If the rand() implementation is particularly poor then it might -be that the bug placement is taking more time.

  • -
  • RESOLUTION #1: It could also be the arrays being updated as the snake gets -bigger.

  • -
  • BOTTOM LINE: I find it unlikely that either of these happen and I don’t know -what to say; you can try decreasing the max size to see if it doesn’t reach this -problem. Otherwise it would be good to see if you can find a pattern: is it when -the snake is bigger i.e. the screen is rather fuller than when you first begin? -Or is the system rather busy?

  • -
- - -

I set SIZE=0 GROW=1, ate a bug but still only the head was visible! Why?

- -
    -
  • RESOLUTION #0: The size includes the head but what should it show if the size -is 0? The head is the obvious choice so once you do get to size 1 you will still -only have the head. When you get to size 2 you’ll have part of the body too. -Try:

    - -
      SIZE=1 GROW=1
    -
    - -

    And you will see that at one bug you’ll have size two and just as if you had - two bugs with the previous invocation.

    - -

    BTW: When you are size 1 you can go back on yourself even if cannibalism is - disabled because there really isn’t anything to go back through: it’s just - you moving the head until you find a body to use! Once you have size >= 2 - then cannibalism mode counts.

  • -
  • BOTTOM LINE: In some senses SIZE=0 and SIZE=1 are the same thing.

  • -
- - -

I like black text on a white background but even with a white background terminal it ends up black!

- -

This is a curses thing but fortunately it should be easy enough to solve.

- -
    -
  • RESOLUTION #0: Try setting the terminal to monochrome for the invocation of -the game. Depending on what is installed on your system you might not have a -monochrome terminal but you can certainly get them. In Red Hat systems these can -be found in the package ncurses-term but maybe that will vary on others. I don’t -recall having to install it under macOS nor Fedora but those systems have GUIs -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.

  • -
  • BOTTOM LINE: Try using a monochrome terminal for the game by e.g.:

    - -
      TERM=linux-m ./prog
    -
    - -

    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).

    - -

    I discuss colours and terminals in much more detail in - terminals.md (terminals.html).

  • -
- - -

The text is hard to see (not bright enough)!

- -
    -
  • RESOLUTION #0: Make sure that your terminal settings have for bold text a -brighter font. The way I have my terminal profiles set up is to use bright -colours for bold text. I specifically enable bold in the program so try and find -out if enabling brighter colours is possible.

    - -

    If it’s already set at brighter colours I do not know what to say. Try - playing with the colours in your terminal, maybe the font size or various - other settings. Probably better to make a new profile for this first in case - you mess up your old one. FWIW I haven’t had this problem on consoles only - and in my systems I have the option to enable brighter colours for bold - it - was only in testing that I came across this problem.

  • -
- - -

Can I move the snake faster temporarily without having to modify the wait time?

- -

Yes. The getch() call is non-blocking when the timeout value is positive so -it’s up to that number of milliseconds.

- -
    -
  • RESOLUTION #0: All you need to do then is hold down the direction key (or for -that matter any other key that getch() will pick up on). You can also rapidly -push the key instead of holding it down.

  • -
  • BOTTOM LINE: Depending on the key repeat rate and/or how fast you repeatedly -hit the key you can have more control over the speed - as long as it is quicker -than the regular wait time. For negative wait time (drawing mode) you have to -hit a key every time to move because it is blocking.

  • -
- - -

On environmental variables more generally and string to int conversions

- -

What happens if you do:

- -
    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 -there is that environmental variable. However the way the strtoul() functions -work is that if there’s no valid input it returns 0. This can be detected but -it’s wasteful (iocccsize wise) and I have never felt that having invalid input -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
-
- -

Would let you go through the walls. On the other hand

- -
    WALLS=test1 ./prog
-
- -

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
-
- -

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
-
- -

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

- -
    SIZE=0
-    SIZE=f
-    SIZE=-
-    SIZE=f1
-
- -

Are all equivalent.

- -

Observe that

- -
    SIZE=08
-
- -

Is invalid input because octal only has digits 0 - 7: the resulting size will be -0.

- -

I discuss the signed/unsigned issue to do with curses and sizes in C being -unsigned in bugs.md but probably there isn’t anything more to say here.

- -

Errors and error reporting

- -

If curses fails to initialise you should get an error that says ‘curses error’ -and you have every right to curse it to hell if that’s what you so wish. -Apparently curses will report an error itself if that function fails but that -escaped my notice until later on so you might get two errors in that case.

- -

Assuming it succeeds the game obtains the maximum y/x of the terminal. The -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 -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
-
- -

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
-
- -

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.

-
-
- - - - -
Creative Commons License

© Copyright 1984-2020, -Leo Broukhis, Simon Cooper, Landon Curt Noll -- All rights reserved
-This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

 
- - diff --git a/bugs.md b/bugs.md index b30bf7859..3a30e9342 100644 --- a/bugs.md +++ b/bugs.md @@ -3601,7 +3601,7 @@ If you have audible bells enabled the program will beep at every turn. ### Information: [2020/ferguson1/README.md](2020/ferguson1/README.md) There are some things that might appear to be bugs but are actually features or -things that are misinterpreted as bugs. See his +things that are misinterpreted as bugs. See the [bugs.md](2020/ferguson1/bugs.md) and [troubleshooting.md](2020/ferguson1/troubleshooting.md) files for details. diff --git a/thanks-for-help.md b/thanks-for-help.md index bdd735afb..20519e210 100644 --- a/thanks-for-help.md +++ b/thanks-for-help.md @@ -4359,7 +4359,8 @@ layered chocolate fudge cake recipe](/2020/ferguson1/chocolate-cake.md) :-) Yes the irony here is as rich as the chocolate cake: the question is do you know how rich it is? If not and you like chocolate I (that is Cody :-) ) highly -recommend you give it a go! :-) +recommend you give it a go! :-) You're welcome to ask me questions if you wish +and I encourage you to do so as the cake is quite picky! ## [2020/ferguson2](/2020/ferguson2/prog.c) ([README.md](/2020/ferguson2/README.md)) @@ -4374,6 +4375,12 @@ Most importantly he also added some corrections to the vital [Double layered chocolate fudge cake recipe](/2020/ferguson2/chocolate-cake.md), enciphered though it is :-) +Yes the irony here is as rich as the chocolate cake: the question is do you know +how rich it is? If not and you like chocolate I (that is Cody :-) ) highly +recommend you give it a go! :-) You're welcome to ask me questions if you wish +and I encourage you to do so as the cake is quite picky! + + ## [2020/kurdyukov2](/2020/kurdyukov2/prog.c) ([README.md](/2020/kurdyukov2/README.md))