Jump to content
Doigt

Retro-ish project: Tetris in the command line

Recommended Posts

So, last Monday I started a little game project as an excuse to experiment with my ConsolePaint library for .Net, but also as an exercise for a much more ambitious project later (which will be fully retro). For this occasion, I thought I'd replicate one of my favourite games of the NES; Tetris, but with a twist; the user should be able to create his own difficulty by directly modifying drop delays, game execution speed and the rate at which the game becomes faster (and thus, more challenging).

There are a couple of challenges in doing a game in the console terminal:

  • Printing characters to the console window is slow.
  • Refreshing the screen is even slower and just printing the characters on new lines wouldn't entirely sell the illusion of a piece moving.
  • You can only use characters when using the terminal, so no graphics (duh!)
  • In this case, the "user interface" needs to be user friendly enough so that anyone can play and alter the game variables without having to remember the command names.
  • There needs to be some kind of sound for tetrises and background music.

I couldn't really solve the first problem since .Net doesn't have any way to hijack the console screen buffer like in C++ with windows.h, so I ended up trying to minimize as much as possible calls to Write and WriteLine by only writing and rewriting in precise areas of the screen by repositioning the cursor. Because some areas of the screen are only drawn once or occasionally, it saved some execution time. This did more to solve the second problem than the first one actually, since it entirely got rid of the slow refresh problem you'd get with the Clear method.

First shot shows the areas where the characters are only printed once on the screen. Second shot shows areas which are only reprinted when the information needs to be updated.
899920200_drawnonce.png.318dd80ec0b321b71811272452_drawnoccasionally.png.a81fc7c

So the thing with Tetris is that the game is basically just squares on a screen. You don't need to be particularly fancy with the graphic design and people know this (except maybe the folks who made Tetris Effect haha). However, I still wanted to use the most out of the character set to make it as pleasant to the eye as possible. This is where the lack of graphics really hurts because the .Net console only supports 16 colours and a limited set of characters. I did notice also that the pieces in classic nes Tetris actually don't always use the same palette and appearance, so I at least wanted to include alternating colours and use fun characters as alternative tetromino blocks. However, after I started showing pictures on a few discord groups and to friends, all were unanimous that the special characters HAD to go and at least half of them found the colours jarring and too much to process. That made me sad because I really liked the fun character set and I sincerely thought the colours made the experience better. But I aim to please everyone, including I. So what I ended up doing is making those things optional; they can be turned on and off in the game settings.

The above screenshot shows a game where the special characters are disabled with colour enabled, this one below is the reverse. These options are not mutually exclusive by the way; you can have grey bland tetrominos or colourful tetrominos using the fun characters.
472755253_optionalfancy.png.342c50341f15
"The fool did not see that he was not Tetris ready. He was severely disappointed!"

Now, there remains the case of the GUI. If I was going to write code for something which wouldn't make people flee, it would take a while. Remember earlier when I wrote that I was looking for an excuse to use my ConsolePaint library? Well, I put it to good use. All these rectangles on the screen were "drawn" by it, I just called the right methods and it wasn't even something I had to worry about. The library can draw all sorts of different borders too and initially I tried a more varied design until I ended up with what we have now. I also used that library to draw a picture on the screen so that the main menu didn't look too empty. What I did first was to draw some quick pixel "art". The colours of the image were converted to the 16 colour palette of the .Net console and then the pixels were converted to characters. Although in this case, the conversion was probably not needed. The image I drew: image.png.391a72cb7c5073866c0617180ed31e

The result:
tetris_main.png.fc2ede6a73d1513e478885b3
You may be surprised to see that I named my Tetris clone "Retro Tetro Pro". That's actually because the name Tetris is trademarked. Maybe I'm being overly cautious, but I want no trouble for a game which only a few number of people are going to play.

I'm not going to talk to much about the user friendliness of the GUI, I think navigating the menus with the arrow keys and pressing enter is a pretty obvious design choice. However it may or may not surprise you to learn that I didn't use a switch statement or if statement for handling user choices in the menu. That would have been too long and tedious! Instead I chose to put some actions in an array and the arrow keys actually increase or decrease an index which is used to call those actions. I wish I had thought of that before. Strictly speaking of design though, the only thing I consciously thought about for more than one second is how to give an obvious visual cue that a value is being increased or decreased. I chose to print a green upward triangle when the user is increasing a value and red downward triangle when the user is decreasing the value. You can see more of the GUI and the effects the options have on the game in this little demonstration video: https://www.youtube.com/watch?v=MuE_L0-kAlI

Those who went ahead and watched the video will notice that the game only plays a sound when its in the menu... That's actually a reflection of the how bad it is to use sounds in .Net. There was nothing I could do to really play music or fun sounds without it being a huge pain in the neck. I tried several solutions and ways to circumvent the problem, but the tools .Net comes with are insufficient for a pleasant experience. I figure if I have enough time and motivation in the future that I'll have to use a custom sound library with .Net bindings; Beeps are too annoying and trial-and-error based to be practical and the SoundPlayer is incredibly frustrating; it has its own little thread and it does its own little thing, so no way to directly interact with it or do "complex" algorithms like having a playlist of music or playing consecutive sounds. It plays the tune until its over or until the process is dead and sometimes its stop method inexplicably doesn't work. Also there's no way to tell when the SoundPlayer is done... not mentioning several limitations like being limited to playing wav files out of anything. At this point, I was a bit tired of working on this project and anyways, to me, one of the rare people on earth who does not enjoy music due to a medical condition, it was the least of my priorities.

Thus, I felt it was a job well done and decided to release the game to a slightly wider audience with the bugged music player (I ended up removing the music altogether in the new version because the obsolete wav files made the download too large). I haven't gotten a lot of feedback yet because well... it's a Tetris clone. Everyone and their grandmother has both played and made it before. So I can't tell you if it was a success from the point of view of others. But I can tell you I'm satisfied with what I came up with despite the fact that I wasn't able to solve every issue I had. I learned a lot of things by putting myself to the challenge of making it work in the terminal window and it was a pretty interesting experience which I don't regret.

Thank you for reading this post. And if you try Tetro Retro Pro, thank you once more, it makes me incredibly happy that it will not all be merely for showing off and experimenting.
You can download the game here. Arrow keys to move and z to rotate.

Edited by Doigt

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Similar Content

    • By ChaosRagnarok100
      Good night everyone, I present to you my first creation of the archetype that I want to create based on the famous saga of horror games from FNAF. Through this forum I will share all my ideas about this project, I would like to know your opinion about this design. Do you like the idea? 👀

       
    • By LordCowCow
      Match ends 12/25 8 PM CST
    • By LordCowCow
      Match ends 12/21 8 PM CST
    • By |0P|
      Pretty much the same as every other "Make a ___ Card" game just with my own (not particularly original) Sub-Type. And because I need something to do when I'm bored I'll try my best to make images to go with any cards that aren't given any and add them here
      Cards Created
      Rules
      1. All monsters must have the "Negative" Subtype and have either the "Negative Clause" (see rule 2) or an effect that allows their Level or Rank to become a negative number OR must directly support/be supported by Negative monsters.
      2. All monsters types can be Negative except Link due to the lack of a Level or similar attribute that can be given a Negative value, Links that require Negatives as tribute or benefit from being linked to them are allowed.
      3. Suggestions can come in the form a card name, a Type/Attribute, an additional Sub-Type, an effect/premise, or any combination of the 4
      beyond that it's the basic rules
      4. If you create a card leave a prompt
      5. You must respond to the most recent prompt only
      (Not a rule, more a design note to keep in mind, Negative monsters tend to have strange effects such as having no Attribute or only being able to be summoned during your opponents turn)
      to get the ball rolling here's some examples of what you can do with Negative monsters
      As for a suggestion how about a monster that has effects based on the combined Levels of all monsters on the field, the closer to zero the better the effect.
    • By Chiri Bedhead
      This section seems like not that much of a thing. It may stay that way, but I thought I'd make a simple game to change that, at least for a bit.
      Like I said, the rules are simple.
      Each post shall be the name of a Pocket Monster beginning with the next letter alphabetically after the previous post, until we reach "Z".
      Afterwards, the person that got the "Z" post will get that post liked (by at least the next poster) and the next post will begin with "A".
      Repeat without repeating Pokemon for the next round (if one round has Mew, the round after the next round can also have Mew, but not the round immediately after it). If it's repeated and someone notices, the next post restarts at "A".
      Also, you cannot post again until at least two other people have posted. That's it!
       
      I'll start:
      Aerodactyl
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...