8 ms·
This reminded me of a game programming hack I did back in highschool. I had just started a school course on Pascal and decided to code a small game of snake, ju
by Morgawr 13y ago
This reminded me of a game programming hack I did back in highschool. I had just started a school course on Pascal and decided to code a small game of snake, just for fun. I knew very little about actual programming, I was a real novice. The game was very simple, it was running in a windows console (cmd) without any graphics, the actual assets were ASCII art. The grid of the game was represented with asterisks and the snake was dots with a smiley face (one of those weird ASCII symbols nobody knows why it's there). Every game update I would redraw the whole grid, snake and the comma that was used to output the food.
The problem was that this was terribly slow, it flickered like crazy and it was unplayable. I was very sad because my game was working but unplayable for anybody so I tried to engineer a way to make it stop flickering. The solution came when I found out about a couple of functions in pascal that let you clear a specific character in the console at a specific X,Y coordinate and write another character that that coordinate. What I ended up doing was keep track of all the changes in the game for each frame (snake movements, food position) and just re-draw only the portions of screen that had changed.
This was great, no more flickering and the game was playable. (Nobody really played it because nobody cared but I was really proud of it).
Found out years later that this approach is pretty much what Carmack did in his old games: Adaptive Tile Refresh[1]
[1]https://en.wikipedia.org/wiki/Adaptive_tile_refresh https://en.wikipedia.org/wiki/Adaptive_tile_refresh
- acheron 13y agoIf you're curious why the smiley faces and such were in MS-DOS extended ASCII: Bill Gates: "... We were also fascinated by dedicated word processors from Wang, because we believed that general-purpose machines could do that just as well. That's why, when it came time to design the keyboard for the IBM PC, we put the funny Wang character set into the machine—you know, smiley faces and boxes and triangles and stuff. We were thinking we'd like to do a clone of Wang word-processing software someday."[1] [1] https://en.wikipedia.org/wiki/Code_page_437 https://en.wikipedia.org/wiki/Code_page_437
- Morgawr 13y agoThat's really interesting! Thanks!
- ketralnis 13y agoI made a similar game for a TI calculator where the issue I was having was a lack of memory for the game state. Eventually I found that you could read the graphics memory, so I stopped saving any of the game state at all: I'd tell if there was an obstacle by just reading the graphics memory and seeing if the according pixel was already black. Now system dialogs like the calculator's "reminders" could draw their dialogues to the screen and become obstacles themselves. Now I couldn't tell food from obstacles, so I just got rid of the food idea and now you grow infinitely, and the randomly dropping obstacles XOR their targets to potentially free up walls to pass through. Like it sounds, the game went through a lot of changes like this in structure through the course of development, where the nature of the game had entirely changed. I had a number of friends with my model of calculator, which was not the more-common TI-86 at my school, so I was the only, err, game in town. Because it was in-effect several games over its lifetime, and different people wanted different versions of it, I learnt a lot about maintainability and version management. "You have the six-month-old version of this with food, and it has a bug that I fixed five months ago in a different game. And the fix doesn't apply here". Or "I solved the slowness you're seeing in this case by removing 3 instructions from inside of this loop here, but that cut the key response time in half, is that alright?" It was a major learning experience for me. My calculator is long-dead and I don't have a version of the game anymore, but I bet that I could still remember how to make changes to it if I saw the code again.
- a3_nm 13y agoI remember writing a naive recursive bruteforce Sudoku solver in TI-BASIC on a TI-82-stats in high school. The "recursive" part was non-trivial, because, though a sub could call itself, there were no local variables, only global variables shared between all the subs. Fortunately, there were global lists that you could use, so I ended up using a global list as the stack, along with a global variable to maintain the nesting depth between the recursive instances and serve as some sort of pointer to the current stack position. It worked; it was so slow it took about one second per call, so you could really see it explore the tree, but it usually managed to solve Sudoku grids in a few hours. I also recall writing minesweeper (this time on a TI voyage 200), also in TI-basic. The main difficulty, of course, was that when you hit an empty square you have to recursively uncover all adjacent empty squares. So I wrote this recursively, but on this TI there were local variables to recursive calls which also meant that the calculator would sometimes run out of memory when doing its recursive calls (at vaguely unpredictable intervals). Fortunately, I discovered that this could be caught with the rudimentary exception-handling mechanism provided, so I would use this to explore not everything but a small radius around the selected position. The puzzling thing, however, was that the radius was uneven, and got smaller and smaller (as if some memory was being leaked by the interpreter), and more surprisingly, in the graphics view that I used, whenever the exception handling would trigger the TI would display a small warning "differentiating an equation may yield a false equation". To this day I have no idea why. It all sounds silly now but I learnt a lot typing naive code for a slow, buggy, and limited interpreted, on a cramped calculator keyboard, during boring classes. If smartphones had existed back then, I wonder what it would have changed.
- acqq 13y agoI'm sorry, but believing you did in the highschool what Carmack managed to achieve on the PC in his early games is delusional. What Carmack achieved (by using the hardware features of the graphic cards of that day) is properly described in the link you give: smooth side scrolling on the PC. The kind of scrolling seen on the consoles of that day, which had a hardware support, or in the famous http://en.wikipedia.org/wiki/Super_Mario_Bros http://en.wikipedia.org/wiki/Super_Mario_Bros (a copy of which is the unreleased "Dangerous Dave in Copyright Infringement" made by Carmack). That has nothing to do with drawing only the changed characters with simple print at the given screen coordinates, what you did. To achieve that, it used exactly hardware feature of changing the origin of the area in memory that is going to be picked up by the graphic chip to be presented on the screen. That changing was made in one pixel increments. Without it, no smooth scrolling. Second, he redraw the tiles, he didn't print the characters. So no, that is absolutely not "pretty much what Carmack did in his old games." What you did at the end was done by absolutely every game at these times (using the commands of cursor movements to position the characters then printing them, in Turbo Pascal using gotoXY from the console library), what you started with (reprinting the whole screen just because) was simply as "pessimized" approach as it can be made at all. You didn't even optimize anything, you did what everybody did, you just started with the "pessimization." I'm old enough to remember. There is also at least somebody here who can provide a link to any computer magazine which printed such games in a few lines of code (at that time sources were made small enough to be printed in the magazines). For the reference, at least the terminals in 1973 already had "Direct Cursor Addressing" -- the technology behind the gotoxy you used: http://vt100.net/docs/vt05-rm/chapter3.html#S3.8 http://vt100.net/docs/vt05-rm/chapter3.html#S3.8 (It was already standardized anyway!) That terminal had 1440 characters, all directly addressable, and the maximum communication speed was 2400 bps which gives 300 characters per second or almost 5 seconds to push the whole screen to it. If you moved for example 10 characters, each with a 5 byte sequence, you needed only 50 bytes, so you can change their position 6 times per second. Everybody at that time knew that stuff. It was slow enough to see.
- Morgawr 13y agoI'm not sure where all the bitterness comes from but obviously I'm not comparing an amateurish attempt at optimizing a terminal-based snake game written in Pascal to the genius of Carmack, those are two entirely different things. And yes, I did that while I was in highschool, which is still years after Carmack did his deed. It was around uh... 2005? I thought I'd just share an interesting "hack" I did when I was a teenager which, to me, felt like a real genius trick to smooth gameplay. Back then my code was literally a lot of messy spaghetti goto hell, so you can understand how much of a novice I was. I recommend re-reading what I said, I never claimed I "pretty much" did what Carmack did, just that the idea behind incremental upgrades was "pretty much" similar to Carmack's genius. No more, no less.
- bencollier49 13y agoAnother way to avoid the flickering was to wait for the screen refresh interrupt before writing to the video buffer, but it did slow things down if you weren't careful.
- ygra 13y agoThe flickering most likely was the result of the most naïve way of redrawing the screen: clrscr(); draw(); This redraws twice, by first clearing and then overwriting again. The flickering should go away completely if you actually redraw the screen, e.g. gotoxy(1, 1); draw(); At least in the things I wrote in Turbo Pascal back in the day this was enough to fix performance problems from drawing too. gotoxy() calls cost time too and simply writing a large bunch of characters in one go is often fast enough. Of course, writing directly to video memory is by far the fastest approach. The last time I had fun with this was with FreePascal in my first semester with a friend. We wrote a text-mode UI library for one assignment (it wasn't strictly necessary, but it was a fun learning experience); it had a message loop that handled focus and dispatched input to controls, full-screen "windows", sub-screen dialogs, and buttons, text input boxes, combo boxes, and tabular view. We used gotoxy() and write() calls throughout, which had the benefit that we could test it on my friend's old VT-100 terminal as well (it was slow as hell, though, but you could literally see where to optimize draw calls, similar to using X remotely over a slow network where you can watch it draw each menu half a dozen times), but planned on moving to a more optimized strategy. Alas, we never really had the time for a v2 rewrite so the project just went to die. And by now it's probably of no use to anyone anyway (but it taught me a lot about how windowing systems work or at least can work).
- nitrogen 13y agoWhat you describe is also similar to a bitmapped graphics technique called dirty rectangles.
- army 13y agoIt's also a specific instance of a more general technique of incremental computation, which is, if you look at it from the right perspective, a more specific instance of integration: rather than computing a function directly, you're computing the differences and then integrating. So you could almost say that the method dates back to the 18th century: http://en.wikipedia.org/wiki/Euler%27s_method http://en.wikipedia.org/wiki/Euler%27s_method. It's definitely one of those tricks that comes in handy now and again :).
- ajuc 13y agoI've done similar game, had same problem, then I found out that you could write directly to the text buffer memory in pascal - the address was B800:0001 if I remember correctly? Every even byte was ASCI code, and odd bytes were color numbers. It was fast enough that I could redraw the whole screen each frame no problem (and I could do nice effects like changing the colors of the screen without changing the content).