Skip to content

Instantly share code, notes, and snippets.

@magiblot
Last active July 22, 2021 18:47
Show Gist options
  • Save magiblot/a08e5e5bdf64695b9b1d7066f6d1eb1c to your computer and use it in GitHub Desktop.
Save magiblot/a08e5e5bdf64695b9b1d7066f6d1eb1c to your computer and use it in GitHub Desktop.
Turbo Vision Snake Example

This is an extremely basic Snake example using Turbo Vision. It doesn't even implement the game logic; it just shows a snake emoji that can be moved with the arrow keys or the mouse.

To build on Unix:

  1. Make sure the following are installed:
    • A C++ compiler (e.g. GCC or Clang).
    • A build generator (e.g. Make or Ninja).
    • git, CMake and Ncurses.
  2. Run:
    git clone https://github.com/magiblot/tvision && (
        cd tvision &&
        cmake . -B ./build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=$HOME/.local &&
        cmake --build ./build -- -j$(nproc) &&
        cmake --install ./build
    )
  3. Place snake.cc and CMakeLists.txt in a clean directory. Then run:
    cmake . -B ./build -DCMAKE_BUILD_TYPE=RelWithDebInfo &&
    cmake --build ./build -- -j$(nproc)
  4. Run ./build/snake.

Screenshot_20210722_203930

cmake_minimum_required(VERSION 3.5)
project(snake)
add_executable(snake snake.cc)
find_package(tvision CONFIG)
target_link_libraries(snake tvision::tvision)
#define Uses_TKeys
#define Uses_TApplication
#define Uses_TDeskTop
#define Uses_TEvent
#define Uses_TMenuBar
#define Uses_TSubMenu
#define Uses_TMenuItem
#define Uses_TStatusLine
#define Uses_TStatusItem
#define Uses_TStatusDef
#include <tvision/tv.h>
class SnakeApp : public TApplication
{
public:
SnakeApp();
static TMenuBar *initMenuBar(TRect);
static TStatusLine *initStatusLine(TRect);
};
class SnakeView : public TView
{
TPoint pos;
public:
SnakeView(const TRect &bounds);
void handleEvent(TEvent &ev) override;
void draw() override;
};
SnakeApp::SnakeApp() :
TProgInit( &SnakeApp::initStatusLine,
&SnakeApp::initMenuBar,
&TApplication::initDeskTop )
{
// Create an instance of our Snake game and place it on the desktop.
TRect r = deskTop->getExtent().grow(-2, -1);
auto *snakeView = new SnakeView(r);
deskTop->insert(snakeView);
}
TMenuBar *SnakeApp::initMenuBar(TRect r)
{
r.b.y = r.a.y + 1;
return new TMenuBar(r,
*new TSubMenu("~F~ile", kbAltF) +
*new TMenuItem("E~x~it", cmQuit, kbNoKey, hcNoContext, "Alt-X")
);
}
TStatusLine *SnakeApp::initStatusLine(TRect r)
{
r.a.y = r.b.y - 1;
return new TStatusLine(r,
*new TStatusDef(0, 0xFFFF) +
*new TStatusItem("~Alt-X~ Exit", kbAltX, cmQuit) +
*new TStatusItem(0, kbF10, cmMenu)
);
}
SnakeView::SnakeView(const TRect &bounds) :
TView(bounds)
{
growMode = gfGrowHiX | gfGrowHiY; // So that it gets resized along the desktop.
options |= ofSelectable; // Otherwise it won't react to key/mouse events.
eventMask |= evKeyDown | evMouseDown | evMouseMove;
pos.x = (bounds.b.x - bounds.a.x)/2; // Display the snake at the center of the screen.
pos.y = (bounds.b.y - bounds.a.y)/2;
}
void SnakeView::handleEvent(TEvent &event)
{
TView::handleEvent(event);
bool handled = true;
switch (event.what)
{
case evKeyDown:
{
switch (event.keyDown.keyCode)
{ // Arrow keys move the snake by one position.
case kbUp:
pos.y = max(pos.y - 1, 0);
break;
case kbDown:
pos.y = min(pos.y + 1, size.y - 1);
break;
case kbLeft:
pos.x = max(pos.x - 2, 0); // The snake emoji is two columns wide,
break; // so it moves by two horizontal positions.
case kbRight:
pos.x = min(pos.x + 2, size.x - 2);
break;
default:
handled = false;
break;
}
break;
}
// Mouse press and drag also move the snake:
case evMouseDown:
case evMouseMove:
pos = makeLocal(event.mouse.where);
break;
default:
handled = false;
break;
}
if (handled)
{
drawView();
clearEvent(event);
}
}
void SnakeView::draw()
{
if (0 <= size.x && 0 <= size.y)
{
TDrawBuffer b;
TColorAttr cSnake {0xcc33ff, 0x33cc33}; // {foreground, background} in RGB.
TColorAttr cBgrnd {0xffcc00, 0xccff33};
for (int x = 0; x < size.x; ++x)
b.moveStr(x, "░", cBgrnd);
writeLine(0, 0, size.x, size.y, b); // Draw the background.
b.moveStr(0, "🐍", cSnake);
writeLine(pos.x, pos.y, 2, 1, b); // Draw the snake.
}
}
int main()
{
SnakeApp app;
app.run();
app.shutDown();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment