This is an entirely new version of the Snake game that includes a lot of new features. This program is based on the PowerShell engines to read keys and show text in color fully described at this thread; you should review such a topic for any detail related to the input and output parts of this program.
Although the most obvious point of this program is that the game is now displayed in color, the most important feature is that now there may be several snakes in the board! The new game may be played by up to four players that controls a different snake each: #1-Yellow snake, #2-Green snake, #3-Magenta snake and #4-Cyan snake. The keys used to control each snake are evenly distributed on the keyboard, so up to four hands can freely press them with no interference: the four arrow keys (Up,Left,Down,Right) for snake #1, "WASD" keys for snake #2, "GVBN" keys for snake #3 and "OKL;" keys (in English keyboards) for snake #4, although this key assignment may be easily changed.
This feature converts the old (boring) single-player game in a very interesting one with up to three opponents and several aspects that improves the competition. You really want to play the game with more than one snake to realize how different and fun it is when compared vs. the original game. Anyway, even a single-player game can be greatly improved using the additional features of this new program.
I called "VIBORAS.bat" this game ("snakes" in Spanish) in order to avoid any variation of the same "snake" word that may confuse it with any other program, and also to indicate that this is a different game. The inter-relation of several víboras in the same board allows a new set of game possibilities. For example, when a food appear in the board, the snake that is closer will eat it and this food will give a speed advantage to reach the next food, excepting if it appears much closer to other snake. When the game begins, the winning (larger) snake most depends on luck (the random positions where the foods appear), but when the snakes are larger and faster, the player that choose the shorter paths to new foods will ultimately win the game. If a non-intrusive mechanism that compensate the loosing snake is introduced, the game may be really fun!
If a snake unexpectedly turns into the path of another one, the other snake will crash and die if the player that controls it is not ready for this sudden change. If two snakes arrives to the same position at same time, the program logic moves the lower numbered one first; this detail may produce different results depending on other factors. For example, in a frontal collision of two snakes, if the number of positions between the snakes is odd, the lower numbered snake will arrive first to the center/collision point and the other snake will crash into it and die, but if the number of separating positions is even, then the lower numbered snake will crash into the other one and it will die instead. A large snake may kill another one if encloses it in a trap, so the other snake have not any free space to keep moving. All these situations may be further enriched or avoided introducing additional features in the game. I selected my own ideas for the method used to implement these features, like the possibility to stop or reverse the snake movement, or introducing different types of food that provides the snakes with new abilities.
Top
The environment in which VIBORAS.bat program run requires a few manual adjustments in order to get the best experience. Although some adjustments could programatically be completed via code, the final result could not be satisfactory, so it's not worth to write such a code.
The main adjustments are the font selected in the cmd.exe
window and the size of the playing board, but these adjustments depends on each other. Smaller fonts allows to use larger boards, but it could be difficult to visually identify individual lines and columns with such a small characters. Larger fonts are easier to view, but they limits the size of the board. The maximum possible size for the board is 128x63. Besides, these adjustment also depends on the maximum resolution of the screen in pixels.
You should always select a square font, like the preinstalled 8x8 raster font. You may also use "Terminal 12x12.FNT" or "Terminal 16x16.FNT" raster font files included in the VIBORAS.zip game package. To install these files copy them into "\Windows\Fonts" folder and answer Yes to "Terminal font is already installed. Replace it?" question.
Top
The basic snake movement control is turn its direction to right or left when the Righ-Arrow or Left-Arrow keys are pressed, respectively, like in the original game. The snake's speed is slightly incremented each time that a food is eaten, like in the original game. Other movement control is possible via Up-Arrow and Down-Arrow keys that selects different movement modes, as described below:
Turbo: | twice the Normal speed ("Turbo" indicator appears). |
⬇ Down - Up ⬆ | (up key requires a Turbo-food, see below) |
Normal: | current speed. |
⬇ Down - Up ⬆ | |
Slow: | half the Normal speed ("Slow" indicator appears). |
⬇ Down - Up ⬆ | |
Stop: | snake don't moves ("Stop" indicator appears). |
⬇ Down (down | key requires a Reverse-food, see below) |
Reverse: | snake moves in reverse direction at Normal speed. |
When a snake is in Stop mode, each press of Left-Arrow or Right-Arrow keys moves the snake one position in such a direction. If there is not a Reverse-Food, each press of Down-Arrow key moves the snake one position forward. Note that if a Reverse-Food is used, the snake moves one position forward before start to move in reverse direction.
If any food is eat in Turbo, Slow or Stop modes, the movement mode automatically reverts to Normal.
Top
This is a description of the different types of food designed for the first version of VIBORAS.bat game.
NOTE: The following types of food are not yet implemented in the current version of VIBORAS.bat program.
If a snake try to eat a Reserved food that don't belongs to it, it will be considered an obstacle and the snake will crash into it.
Top
There are several ways to make the different types of food to appear. For this mechanism, the foods are divided in two classes.
Snakes=1, StdFoods=1
is the definition of the original Snake game.
Snakes=2, StdFoods=1
are two snakes fighting for just one food each time.
Snakes=2, StdFoods=2
are two snakes fighting for two foods, that may be eaten by any snake.
Snakes=2, ResFoods=1
are two snakes, each one eating its own food. No fight for food.
Snakes=2, StdFoods=1, ResFoods=1
are two snakes, each one with one food reserved for it, plus one common food to fight for.
Snakes=3, StdFoods=1
are three snakes fighting for just one food each time.
Snakes=3, ResFoods=1
are three snakes, each one eating its own food; no fight for food.
Et cetera: any other combination in the number of Snakes, Standard and Reserved foods works in the same way.
The foods in this class appear on the screen at variable time intervals that are specified based on other conditions. The way to define these conditions is via a SET /A
command arithmetic expression that returns a number each time it is evaluated. To do that, this food expression may use some dynamic variables, like the predefined random
one or other interface variables designed for this purpose.
To enable the other types of foods and features you should assign to the desired variable a SET /A
arithmetic expression; the result of this expression may be any number, but if the feature is of "Enable/Disable" type, a zero means Disable and any value different from zero means Enable.
Remember that the value assigned to these variables is a string that contains an arithmetic expression, that is, the part placed after SET /A
. In this expression a single %
percent-sign means the modulo (remainder) operator, but the !
exclamation-mark character requires a special treatment due to the method used to inject these expressions in the running program. If you want to expand the value of a variable use a delayed expansion in this way: ^!variable^!
, although this is only required in certain cases like the Batch predefined dynamic variables. To use the !
(boolean not) operator in an expression, use this construct: ^^^!
.
The food expression may use other features, like conditional expressions and interface variables.
These are numeric expressions that gives an equivalent result of individual IF
conditional commands.
Conditional command | Equivalent expression |
---|---|
IF (A equ B) R=then ELSE R=else |
cond=^^^!(A-B), R=cond*then+^^^!cond*else |
IF (A neq B) R=then ELSE R=else |
cond=^^^!^^^!(A-B), R=cond*then+^^^!cond*else |
IF (A geq B) R=then ELSE R=else |
cond=(A-B>>31)+1, R=cond*then+^^^!cond*else |
IF (A lss B) R=then ELSE R=else |
cond=^^^!((A-B>>31)+1), R=cond*then+^^^!cond*else |
To perform the gtr
and leq
missing conditions, adjust one operand and use another given condition. For example, to get a IF (A gtr 10)
condition, just use the equivalent IF (A geq 11)
expression.
You may also combine two or more conditions via &
(AND) and |
(OR) bitwise operators. For example, this condition: IF (A equ B and C geq D)
can be written in this way: c1=^^^!(A-B), c2=(C-D>>31)+1, cond=c1&2
or the simpler cond=^^^!(A-B)&(C-D>>31)+1
.
For further details on the operators that can be used in these expressions, review the description of SET /A
Batch command.
Besides random
and time
predefined Batch variables, you may use the following inteface variables in the food expressions:
Variable | Description |
---|---|
init | Initialize persistent variables. See below. |
seconds | Game elapsed time in seconds. |
size[1..4] | Array with the size of each snake. |
del[1..4] | Array with the delay (1/speed) of each snake. |
maxSize | Size of the largest snake. |
minSize | Size of the smallest snake. |
exit | Specify when to exit a game. |
The exit variable indicate the condition that marks the end of a game: 0 = When the first snake crash. 1 = When the last snake crash, removing previously crashed snakes. 2 = When the last snake crash, keeping previously crashed snakes as obstacles.
NOTE: The exit variable is not yet implemented in the current version of VIBORAS.bat program.
If the expression uses additional auxiliary variables, it is suggested to select a1,a2,a3,...
names for temporary variables and n1,n2,n3,...
names for persistent variables that will keep their values between program cycles; you may use init interface variable to initialize persistent variables when the game begins. For example: set "init=n1=0, n2=10, n3=0"
.
Note that when several variables are assigned in a long expression, the final result is the value of the first variable; for example: set "AnyVar=a1=10,a2=20,a3=a1+a2"
sets AnyVar to 10
. To get the value of the last variable, just enclose the whole expression in parentheses: set "AnyVar=(a1=10,a2=20,a3=a1+a2)"
sets AnyVar to 30
; note also that the last variable is not needed: set "AnyVar=(a1=10,a2=20,a1+a2)"
StdBoost is an option of Standard-food. When a Standard-food appear, it may appear as a Boost-food instead, that is, with a value greater than one; this behavior is controlled by the value given by StdBoost food expression.
set "StdBoost=0"
Is the same as not include it: the Standard-food will always appear with a value of 1.
set "StdBoost=6"
All Standard-foods will appear with a value of 6.
set "StdBoost=^!random^!%10"
Standard-foods will appear with a random value between 1 and 9.
set "StdBoost=(a1=^!random^!%10, cond=(a1-5>>31)+1, cond*a1)"
This condition uses the IF (A geq B)
equivalent expression described above. Half of the Standard-foods will have a value of 1, and the other half will have a random value between 5 and 9. Note that cond
variable is not needed: set "StdBoost=(a1=^!random^!%10,((a1-5>>31)+1)*a1)"
do the same.
set "StdBoost=seconds/60"
The boost value increases every minute of playing time.
Remember that any boost value will be automatically limited to 9.
IMPORTANT: Note that, unlike StdBoost and ResBoost variables that are evaluated just when a new food is needed, the rest of variables are re-evaluated once in each program cycle, that is, several times per second. This means that it is necessary to include some kind of repeat control in the expression of those variables; otherwise a lot of new foods will be created until the initial condition change.
Also, remember that all *Res variables will automatically assign their results to the smallest snake(s) only.
For example, suppose that a Boost food, reserved just for the smallest snake (BoostRes), should be created with a value of 7 every 3 minutes. Apparently, this is simple: set "BoostRes=^^^!(seconds%180)*7"
, that is, get the seconds%180
remainder of the division of current seconds by 3 minutes (that is zero every exact multiple of 180 seconds), apply ^^^!
boolean NOT operator to it, so it gives 1 in this case and 0 with any other remainder, and finally multiply it by 7. Easy! Isn't it? ;).
However, this expression will create a new food in each program cycle that is executed in the same second! As a matter of fact, this is a very simple way to know how fast is a computer: just count the number of foods that were created in one second (in my old and slow computer this number was 36).
The way to fix this point is via an additional condition that prevents to create more foods until a reset condition occurs; the additional condition should be managed in a persistent variable and should not conflict with the original condition. In this case, the reset condition could be the opposite one of the original condition. This is a way to assemble such expression:
n1=1
Initialize: the value of n1
will enable/disable the creation of new foods.
c1=^^^!(seconds%180)
Condition: is 1 every time multiple of 3 minutes.
c2=n1&c1
Condition: is 1 when a new food must be created, that is, when new foods are enabled and the time is multiple of 3 minutes. At same time, the creation of new foods must be disabled: n1=0
.
In this way, after a new food was created, the value of n1
must stay in 0 until the remainder be not zero again. One way to express such a condition is this: n1=n1-c2+^^^!n1*^^^!c1
, that is, after n1=1
was initialized, this formula keep such value for 3 minutes. Then, the first time that c2
is 1, this formula changes the value of n1
to 0. After that, the value of c2
will be 0 and the value of ^^^!n1*^^^!c1
will also be zero all the times that this formula be repeated in the same second. When the second changes, c1
changes to zero, so ^^^!n1*^^^!c1
part will give 1. At that moment, the formula changes n1
to 1 again.
The final food expression must be written in this way:
set "init=n1=1" set "BoostRes=( c1=^^^!(seconds%180), c2=n1&c1, n1+=^^^!n1*^^^!c1-c2, c2*7 )"
Using the same approach, now suppose that a new BoostRes food must be created with a value of 5 when the difference in size between the smallest and the largest snakes be 10 or greater, and that the creation of new foods must be cancelled until this difference falls below 8. The expression to do that can be assembled based on the following subexpressions:
n1=1
Initialize: the value of n1
will enable/disable the creation of new foods.
c1=((maxSize-minSize)-10>>31)+1
Condition: is 1 when the difference in size is greater than 10.
c2=n1&c1
Condition: is 1 when a new food must be created, that is, when new foods are enabled and the difference in size is greater than 10. At same time, the creation of new foods must be disabled: n1=0
.
c3=^^^!(((maxSize-minSize)-8>>31)+1)
Condition: is 1 when the difference in size is less than 8.
c4=^^^!n1&c3
Condition: is 1 when the creation of new foods must be enabled: n1=1
, that is, when new foods are disabled and the difference in size is less than 8.
Note that c2
and c4
may be both 0 or just one can be 1; they can not be both 1 at same time. In this way, the updated value of n1
is given by: n1=n1-c2+c4
; that is:
n1
is 1, then c4
is always zero and c2
may be 1; at that moment: n1=0
.
n1
is 0, then c2
is always zero and c4
may be 1; at that moment: n1=1
.
The final food expressions for this Reserved-Food can be written in this way:
set "init=n1=1" set "BoostRes=( a1=maxSize-minSize, c1=(a1-10>>31)+1, c2=n1&c1, c3=^^^!((a1-8>>31)+1), c4=^^^!n1&c3, n1+=c4-c2, c2*5 )"
The way to activate all these features is placing the desired food expressions in a file with .txt extension and then run the VIBORAS.bat program with the name of such a file as parameter. There are some examples of *.txt game specification files included in the VIBORAS.zip game package.
The simplest way to understand how all these features work is just modify the value of some expressions and play the game again until the purpose of each feature be understood. In this way, you may write your own game specification files that fulfills your desires or needs.
Top
A snake may reproduce using the following procedure:
NOTE: Snake reproduction is not yet implemented in the current version of VIBORAS.bat program.
Top
This program was developed with the goal of made the code and logic as short as possible, making good use of complex data structures. These are the relevant points in the program design:
Coordinates: VerticalHorizontal Bits: 12....76.....0 Vertical: 6 bits, Horizontal: 7 bits Last position: 1111101111111 Vertical=62, Horizontal=127 Hexadecimal: 1 F 7 F Decimal=8063
This means that all board positions can be represented by a single number in the 0..8063 range. The first board position, at coordinates (0,0), is represented by number 0. The last board position, at coordinates (127,62), is represented by number 8063 as shown in previous scheme. The conversions formulae are: SET /A "position=(row<<7)+col"
and SET /A "row=position>>7, col=position%%128"
.
IF defined array[%headPos%] ...
command, and such an element is deleted after processed. For example, when a reserved food appears, an element of "reserved" array is created: SET "reserved[%position%]=%snake%"
; the same is done with the other special types.SET /A "toRight=1, toLeft=-1, toDown=128, toUp=-128"
.SET /A "headPos+=headMov, tailPos+=tailMov"
.SET /A "newMov=turn[%oldMov%,%keyPressed%]"
. Turn array is initialized this way: SET /A "turn[%toRight%,%RightKey%]=toDown, turn[%toRight%,%LeftKey%]=toUp,
turn[%toUp%,%RightKey%]=toRight, turn[%toUp%,%LeftKey%]=toLeft"
, etc.SET "node[%headPos%]=%headMov%"
. Each time that the snake tail reaches a node position: IF defined node[%tailPos%]
, the tail movement is changed: SET /A "tailMov=node[%tailPos%]"
and the node is deleted. This method allows to store a large snake in a few nodes.
Note that all visual elements of the game are displayed on the screen in the proper colors using the PowerShell code as explained elsewhere. This means that it is not necessary to store the screen contents in a variable. There are several minor variables that aids to complete this display, like the color of each snake, the characters used to display the snake bodies, etc.
Most of these points were already implemented in the first version of my Snake.bat program. These data structures allows a simpler management of one snake via a few single number variables, so it is just necessary to convert these variables into arrays in order to manage several snakes. As a matter of fact, this program is capable of animate many snakes with no problems (about 30-40 snakes if we take the Spiral in color program as comparison), so the real limitation in this case is the way to give input keys to control such an amount of snakes with no interference among them. The current version of this program responds with no problems to the simultaneous requests of up to four players as long as the keys are pressed and released in a quick and natural way.
Note that this is work in progress and that the current VIBORAS.bat program is a preliminary version. There are several planned features that are not yet implemented. Also, there are several obvious features that are missing, like the record of highest scores or the storage of a game in a disk file in order to continue it later. All these features should appear in future versions.
Antonio