Playing Minecraft in the Terminal
Well there is a reason that you clicked to view this blog post and a reason I wrote this. We all hate good graphics. We love the olden golden days. Days where the visuals were a delight and not to harsh on the GPU. Playing Pong provides the same fun in just 3KB or less as compared to Cyberpunk 2077, if it weren't that buggy. Alas! We need worse graphics and I am there to help you.
Jokes aside, playing Minecraft in the terminal is quite a big project on its own. Although, I wanted a software that would be help me run my computer using ASCII characters. These characters would be printed on the screen and thus I would be able to play Minecraft, watch YouTube and browse Quora just through the terminal.
Plan
To make this software versatile, we need a dual display set up. Why? Because it makes our lives much easier. We can run our application on the first screen and our terminal on the second. Although, many of you may not have such a setup, even I don't. I used an app called Spacedesk which create server on your laptop or computer and then using the client app on your tablet or iPad you will be able to create a multiple monitor setup. Its simple and lite.
Next we shall refer to a certain portion of our screen, convert it to ASCII, print it on the terminal and clear the screen. Simple? Lets see.
Grabbing the Screen
To grab the portion of our screen we shall make use of the Python Image
Library or Pillow. Lets import the library first.
import PIL.ImageGrab
Now grab the screen. Here 'bbox' is the portion of the screen we are referring
too. This will return an image
image = PIL.ImageGrab.grab(bbox=(start_x, start_y, end_x, end_y), all_screens=True)
For me following are the coordiantes
image = PIL.ImageGrab.grab(bbox=(-1024, 0,0, 768), all_screens=True)
ASCII Converter
Now we should convert the image to an ASCII string. To do this we will first
resize the image to fit our terminal. The new height of the image must be the
number of lines our terminal has. Next, we find the ratio between the height
and the width. Lets put this in a function.
def ascii(image, new_height): width, height = image.size ratio = height/width/2 new_width = int(new_height/ratio) resized_image = image.resize((new_width, new_height))
Now you may be wondering, why we are dividing by 2 in the ratio declaration line. The answer is quite simple. Since characters on the screen have an unequal height and width and are not a square, the ratio between the height and width of the ASCII characters must divided, otherwise the image will be not in proportion. In our image, the pixels will be rectangular and not square.
Now lets convert this image to a gray-scaled one.
... gray_image = resized_image.convert("L")
To convert this into an ASCII image, we will analyze the image's pixels. Since
the image is gray-scale, some pixels will be darker and some lighter. We can
replicate this with ASCII characters, since some appear to occupy more space
than the other and appear darker. We shall declare a list of such characters.
ascii_chars = ["@", "#", "S", "%","?", "*", "+", ";", ":", ",", "."]
Now we shall create a string that corresponds to the pixel darkness value of
the image.
... pixel_data = gray_image.getdata() string = "".join([ascii_chars[pixel//25] for pixel in pixel_data])
All that is left, is to add new line after a certain number of
characters that is, the new_width.
... length = len(string) ascii_string = "\n".join([string[i:(i+new_width)] for i in range(0, length, new_width)]) return ascii_string
Making it Run
Now we will simply put all this in a while loop, get the image, pass it
through our ascii() function, print it and clear the screen.
import os
while True: image = PIL.ImageGrab.grab(bbox=(start_x, start_y, end_x, end_y), all_screens=True) ascii = ascii(ascii, height) print(ascii) os.system('cls')# If you are on Mac or Linux write os.system('clear')
This is how it looks.
Note
Lets be serious, its too pixelated. We can fix that by simply keeping the font size on the terminal at its lowest.
If you actually run this, you can see the screen getting cleared and the characters getting printed. The frame rate is terrible, cause you are forcing the CPU to take on the job of the GPU! On my GitHub page there will be a bit more optimized version of this, that is a teeny-bit better. Till then,
Happy CPU-ing!!
Comments